멘션

멘션 (Mentions)

사람이나 특정 대상을 언급(멘션)해야 할 때 쓰는 컴포넌트예요. @ 같은 접두사를 입력하면 제안 목록을 보여 줘요.

출처: 문서

본문

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

  • 누군가 또는 무언가를 언급해야 할 때 사용해요.

예시 (Examples)

기본 (Basic)

기본 사용법이에요.

import React from 'react';
import { Mentions } from 'antd';
import type { GetProp, MentionProps } from 'antd';

type MentionsOptionProps = GetProp<MentionProps, 'options'>[number];

const onChange = (value: string) => {
  console.log('Change:', value);
};

const onSelect = (option: MentionsOptionProps) => {
  console.log('select', option);
};

const App: React.FC = () => (
  <Mentions
    style={{ width: '100%' }}
    onChange={onChange}
    onSelect={onSelect}
    defaultValue="@afc163"
    options={[
      {
        value: 'afc163',
        label: 'afc163',
      },
      {
        value: 'zombieJ',
        label: 'zombieJ',
      },
      {
        value: 'yesmeck',
        label: 'yesmeck',
      },
    ]}
  />
);

export default App;

크기 (Size)

size 속성으로 크기를 설정해요.

import React from 'react';
import { Flex, Mentions } from 'antd';

const App: React.FC = () => (
  <Flex vertical gap="medium">
    <Mentions size="large" placeholder="large size" />
    <Mentions placeholder="default size" />
    <Mentions size="small" placeholder="small size" />
  </Flex>
);

export default App;

변형 (Variants)

Mentions의 변형에는 네 가지가 있어요. outlined, filled, borderless, underlined.

import React from 'react';
import { Flex, Mentions } from 'antd';

const App: React.FC = () => (
  <Flex vertical gap={12}>
    <Mentions placeholder="Outlined" />
    <Mentions placeholder="Filled" variant="filled" />
    <Mentions placeholder="Borderless" variant="borderless" />
    <Mentions placeholder="Underlined" variant="underlined" />
  </Flex>
);

export default App;

비동기 로딩 (Asynchronous loading)

비동기 방식이에요.

import React, { useCallback, useRef, useState } from 'react';
import { Mentions } from 'antd';
import { createStyles } from 'antd-style';
import debounce from 'lodash/debounce';

const useStyles = createStyles((props) => {
  const { css, cssVar } = props;
  return {
    optionItem: css`
      position: relative;
    `,
    avatarImage: css`
      width: 20px;
      height: 20px;
      margin-inline-end: ${cssVar.marginXS};
    `,
  };
});

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

  const [loading, setLoading] = useState(false);
  const [users, setUsers] = useState<{ login: string; avatar_url: string }[]>([]);
  const ref = useRef<string>(null);

  const loadGithubUsers = (key: string) => {
    if (!key) {
      setUsers([]);
      return;
    }

    fetch(`https://api.github.com/search/users?q=${key}`)
      .then((res) => res.json())
      .then(({ items = [] }) => {
        if (ref.current !== key) {
          return;
        }
        setLoading(false);
        setUsers(items.slice(0, 10));
      });
  };

  const debounceLoadGithubUsers = useCallback(debounce(loadGithubUsers, 800), []);

  const onSearch = (search: string) => {
    console.log('Search:', search);
    ref.current = search;
    setLoading(!!search);
    setUsers([]);

    debounceLoadGithubUsers(search);
  };

  return (
    <Mentions
      style={{ width: '100%' }}
      loading={loading}
      onSearch={onSearch}
      options={users.map(({ login, avatar_url: avatar }) => ({
        key: login,
        value: login,
        className: styles.optionItem,
        label: (
          <>
            <img
              className={styles.avatarImage}
              draggable={false}
              src={avatar}
              title={login}
              alt={login}
            />
            <span>{login}</span>
          </>
        ),
      }))}
    />
  );
};

export default App;

Form과 함께 (With Form)

제어 모드로, 예를 들어 Form과 함께 사용해요.

import React from 'react';
import { Button, Form, Mentions, Space } from 'antd';

const { getMentions } = Mentions;

const formItemLayout = {
  labelCol: { span: 6 },
  wrapperCol: { span: 16 },
};

const App: React.FC = () => {
  const [form] = Form.useForm();

  const onReset = () => {
    form.resetFields();
  };

  const onFinish = async () => {
    try {
      const values = await form.validateFields();
      console.log('Submit:', values);
    } catch (errInfo) {
      console.log('Error:', errInfo);
    }
  };

  const checkMention = async (_: any, value: string) => {
    const mentions = getMentions(value);

    if (mentions.length < 2) {
      throw new Error('More than one must be selected!');
    }
  };

  return (
    <Form form={form} layout="horizontal" onFinish={onFinish} {...formItemLayout}>
      <Form.Item name="coders" label="Top coders" rules={[{ validator: checkMention }]}>
        <Mentions
          rows={1}
          options={[
            {
              value: 'afc163',
              label: 'afc163',
            },
            {
              value: 'zombieJ',
              label: 'zombieJ',
            },
            {
              value: 'yesmeck',
              label: 'yesmeck',
            },
          ]}
        />
      </Form.Item>
      <Form.Item name="bio" label="Bio" rules={[{ required: true }]}>
        <Mentions
          rows={3}
          placeholder="You can use @ to ref user here"
          options={[
            {
              value: 'afc163',
              label: 'afc163',
            },
            {
              value: 'zombieJ',
              label: 'zombieJ',
            },
            {
              value: 'yesmeck',
              label: 'yesmeck',
            },
          ]}
        />
      </Form.Item>
      <Form.Item label={null}>
        <Space wrap>
          <Button htmlType="submit" type="primary">
            Submit
          </Button>
          <Button htmlType="button" onClick={onReset}>
            Reset
          </Button>
        </Space>
      </Form.Item>
    </Form>
  );
};

export default App;

트리거 접두사 커스터마이즈 (Customize Trigger Token)

prefix props로 트리거 접두사를 커스터마이즈해요. 기본값은 @이고 Array<string>도 지원해요.

import React, { useState } from 'react';
import { Mentions } from 'antd';
import type { MentionsProps } from 'antd';

const MOCK_DATA = {
  '@': ['afc163', 'zombiej', 'yesmeck'],
  '#': ['1.0', '2.0', '3.0'],
};

type PrefixType = keyof typeof MOCK_DATA;

const App: React.FC = () => {
  const [prefix, setPrefix] = useState<PrefixType>('@');

  const onSearch: MentionsProps['onSearch'] = (_, newPrefix) => {
    setPrefix(newPrefix as PrefixType);
  };

  return (
    <Mentions
      style={{ width: '100%' }}
      placeholder="input @ to mention people, # to mention tag"
      prefix={['@', '#']}
      onSearch={onSearch}
      options={(MOCK_DATA[prefix] || []).map((value) => ({
        key: value,
        value,
        label: value,
      }))}
    />
  );
};

export default App;

비활성화 또는 읽기 전용 (disabled or readOnly)

disabled와 readOnly를 설정해요.

import React from 'react';
import { Mentions } from 'antd';

const options = ['afc163', 'zombiej', 'yesmeck'].map((value) => ({
  value,
  key: value,
  label: value,
}));

const App: React.FC = () => (
  <>
    <div style={{ marginBottom: 10 }}>
      <Mentions
        style={{ width: '100%' }}
        placeholder="this is disabled Mentions"
        disabled
        options={options}
      />
    </div>
    <Mentions
      style={{ width: '100%' }}
      placeholder="this is readOnly Mentions"
      readOnly
      options={options}
    />
  </>
);

export default App;

배치 (Placement)

제안 목록의 배치를 바꿔요.

import React from 'react';
import { Mentions } from 'antd';

const App: React.FC = () => (
  <Mentions
    style={{ width: '100%' }}
    placement="top"
    options={[
      {
        value: 'afc163',
        label: 'afc163',
      },
      {
        value: 'zombieJ',
        label: 'zombieJ',
      },
      {
        value: 'yesmeck',
        label: 'yesmeck',
      },
    ]}
  />
);

export default App;

팝업 커스터마이즈 (Customize Popup)

popupRender로 드롭다운 메뉴 렌더링을 커스터마이즈해요.

import React from 'react';
import { Divider, Mentions, theme } from 'antd';

const App: React.FC = () => {
  const { token } = theme.useToken();
  return (
    <Mentions
      style={{ width: '100%' }}
      popupRender={(menu) => (
        <>
          <div
            style={{
              padding: `${token.paddingXS}px ${token.paddingSM}px`,
              fontWeight: token.fontWeightStrong,
              color: token.colorTextDescription,
            }}
          >
            Custom Header
          </div>
          <Divider style={{ margin: `${token.marginXXS}px 0` }} />
          {menu}
        </>
      )}
      options={[
        {
          value: 'afc163',
          label: 'afc163',
        },
        {
          value: 'zombieJ',
          label: 'zombieJ',
        },
        {
          value: 'yesmeck',
          label: 'yesmeck',
        },
      ]}
    />
  );
};

export default App;

지우기 아이콘 (With clear icon)

지우기 버튼을 커스터마이즈해요.

import React, { useState } from 'react';
import { CloseSquareFilled } from '@ant-design/icons';
import { Mentions } from 'antd';

const App: React.FC = () => {
  const [value, setValue] = useState('hello world');
  return (
    <>
      <Mentions value={value} onChange={setValue} allowClear />
      <br />
      <br />
      <Mentions
        value={value}
        onChange={setValue}
        allowClear={{ clearIcon: <CloseSquareFilled /> }}
      />
      <br />
      <br />
      <Mentions value={value} onChange={setValue} allowClear rows={3} />
    </>
  );
};

export default App;

autoSize

높이 자동 크기 조정이에요.

import React from 'react';
import { Mentions } from 'antd';

const App: React.FC = () => (
  <Mentions
    autoSize
    style={{ width: '100%' }}
    options={[
      {
        value: 'afc163',
        label: 'afc163',
      },
      {
        value: 'zombieJ',
        label: 'zombieJ',
      },
      {
        value: 'yesmeck',
        label: 'yesmeck',
      },
    ]}
  />
);

export default App;

상태 (Status)

status로 Mentions에 상태를 추가해요. error 또는 warning이 될 수 있어요.

import React from 'react';
import { Mentions, Space } from 'antd';
import type { GetProp, MentionProps } from 'antd';

type MentionsOptionProps = GetProp<MentionProps, 'options'>[number];

const onChange = (value: string) => {
  console.log('Change:', value);
};

const onSelect = (option: MentionsOptionProps) => {
  console.log('select', option);
};

const App: React.FC = () => {
  const options = [
    {
      value: 'afc163',
      label: 'afc163',
    },
    {
      value: 'zombieJ',
      label: 'zombieJ',
    },
    {
      value: 'yesmeck',
      label: 'yesmeck',
    },
  ];

  return (
    <Space vertical>
      <Mentions
        onChange={onChange}
        onSelect={onSelect}
        defaultValue="@afc163"
        status="error"
        options={options}
      />
      <Mentions
        onChange={onChange}
        onSelect={onSelect}
        defaultValue="@afc163"
        status="warning"
        options={options}
      />
    </Space>
  );
};

export default App;

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

classNames와 styles로 객체나 함수를 전달해 Mentions의 시맨틱 DOM 스타일을 커스터마이즈할 수 있어요. 예를 들어 textarea를 크기 조절 가능하게 설정해요.

import React from 'react';
import { Flex, Mentions } from 'antd';
import type { GetProp, MentionsProps } from 'antd';
import { createStyles } from 'antd-style';

const useStyles = createStyles(({ token }) => ({
  root: {
    border: `${token.lineWidth}px ${token.lineType} ${token.colorPrimary}`,
    borderRadius: token.borderRadiusLG,
    width: 300,
  },
}));

const options: MentionsProps['options'] = [
  { value: 'afc163', label: 'afc163' },
  { value: 'zombieJ', label: 'zombieJ' },
  { value: 'meet-student', label: 'meet-student' },
  { value: 'thinkasany', label: 'thinkasany' },
];

const stylesObject: MentionsProps['styles'] = {
  textarea: {
    fontSize: 14,
    resize: 'vertical',
    fontWeight: 200,
  },
};

const stylesFunction: MentionsProps['styles'] = (
  info,
): GetProp<MentionsProps, 'styles', 'Return'> => {
  if (info.props.variant === 'filled') {
    return {
      root: {
        border: '1px solid #722ed1',
      },
      popup: {
        border: '1px solid #722ed1',
      },
    };
  }
};

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

  const sharedProps: MentionsProps = {
    options,
    classNames,
  };

  return (
    <Flex vertical gap="medium">
      <Mentions {...sharedProps} styles={stylesObject} placeholder="Object" rows={2} />
      <Mentions {...sharedProps} styles={stylesFunction} variant="filled" placeholder="Function" />
    </Flex>
  );
};

export default App;

API

공통 props는 Common props를 참고해요.

Mention

속성 (Property) 설명 (Description) 타입 (Type) 기본값 (Default) 버전 (Version) 글로벌 설정
allowClear 멘션 콘텐츠를 지우는 지우기 아이콘을 표시할지 여부 boolean | { clearIcon?: ReactNode, disabled?: boolean } false 5.13.0, disabled: 6.4.0 6.4.0
autoSize Textarea 높이 자동 크기 조정 기능이에요. true | false 또는 객체 { minRows: 2, maxRows: 6 }로 설정할 수 있어요. boolean | object false ×
classNames 컴포넌트 내부의 각 시맨틱 구조에 대한 class를 지정해요. 객체 또는 함수를 지원해요. Record<SemanticDOM, string> | (info: { props })=> Record<SemanticDOM, string> - 6.0.0
defaultValue 기본값 string - ×
filterOption 옵션 필터 로직 커스터마이즈 false | (input: string, option: OptionProps) => boolean - ×
getPopupContainer 제안 목록의 마운트 HTML 노드를 설정해요. () => HTMLElement - ×
notFoundContent 일치하지 않을 때 멘션 콘텐츠를 설정해요. ReactNode No data ×
placement 팝업 배치 설정 top | bottom bottom ×
popupRender 드롭다운 메뉴 렌더링 커스터마이즈 (menu: React.ReactElement) => ReactNode - 6.6.0 ×
prefix 트리거 접두사 키워드 설정 string | string[] @ ×
split 선택된 멘션 앞뒤의 분리 문자열 설정 string ×
size 입력 상자의 크기 large | medium | small - ×
status 검증 상태 설정 'error' | 'warning' | 'success' | 'validating' - 4.19.0 ×
validateSearch 트리거 검색 로직 커스터마이즈 (text: string, props: MentionsProps) => void - ×
value 멘션의 값 설정 string - ×
variant Input의 변형 outlined | borderless | filled | underlined outlined 5.13.0 | underlined: 5.24.0 5.19.0
onBlur 멘션이 포커스를 잃을 때 트리거돼요. () => void - ×
onChange 값이 변경될 때 트리거돼요. (text: string) => void - ×
onClear 지우기 버튼을 클릭할 때 콜백 () => void - 5.20.0 ×
onFocus 멘션이 포커스를 얻을 때 트리거돼요. () => void - ×
onResize textarea 크기가 조정될 때 트리거되는 콜백 함수 function({ width, height }) - ×
onSearch prefix가 감지될 때 트리거돼요. (text: string, prefix: string) => void - ×
onSelect 사용자가 옵션을 선택할 때 트리거돼요. (option: OptionProps, prefix: string) => void - ×
onPopupScroll 멘션이 스크롤될 때 트리거돼요. (e: Event) => void - 5.23.0 ×
options 옵션 설정 Options [] 5.1.0 ×
styles 컴포넌트 내부의 각 시맨틱 구조에 대한 인라인 스타일을 지정해요. 객체 또는 함수를 지원해요. Record<SemanticDOM, CSSProperties> | (info: { props })=> Record<SemanticDOM, CSSProperties> - 6.0.0

Mention 메서드 (Mention methods)

이름 (Name) 설명 (Description)
blur() 포커스 제거
focus() 포커스 획득

Option

속성 (Property) 설명 (Description) 타입 (Type) 기본값 (Default)
value 선택 시 삽입되는 값 string -
label 옵션의 제목 React.ReactNode -
key 옵션의 키 값 string -
disabled 선택 가능 여부 boolean -
className className string -
style 옵션의 스타일 React.CSSProperties -

시맨틱 DOM (Semantic DOM)

시맨틱 DOM 구조는 https://ant.design/components/mentions/semantic.md 에서 확인할 수 있어요.

디자인 토큰 (Design Token)

컴포넌트 토큰 (Mentions) (Component Token)

토큰 이름 (Token Name) 설명 (Description) 타입 (Type) 기본값 (Default Value)
activeBg 입력 상자가 활성화될 때 배경색 string #ffffff
activeBorderColor 활성 테두리 색 string #1677ff
activeShadow 활성 시 box-shadow string 0 0 0 2px rgba(5,145,255,0.1)
addonBg addon의 배경색 string rgba(0,0,0,0.02)
controlItemWidth 메뉴 항목의 높이 string | number 100
dropdownHeight 팝업의 높이 string | number 250
errorActiveShadow 오류 상태에서 활성 시 box-shadow string 0 0 0 2px rgba(255,38,5,0.06)
hoverBg 입력 상자를 hover할 때 배경색 string #ffffff
hoverBorderColor hover 테두리 색 string #4096ff
inputFontSize 글자 크기 number 14
inputFontSizeLG 큰 글자 크기 number 16
inputFontSizeSM 작은 글자 크기 number 14
paddingBlock 입력의 세로 패딩 number 4
paddingBlockLG 큰 입력의 세로 패딩 number 7
paddingBlockSM 작은 입력의 세로 패딩 number 0
paddingInline 입력의 가로 패딩 number 11
paddingInlineLG 큰 입력의 가로 패딩 number 11
paddingInlineSM 작은 입력의 가로 패딩 number 7
warningActiveShadow 경고 상태에서 활성 시 box-shadow string 0 0 0 2px rgba(255,215,5,0.1)
zIndexPopup 팝업의 z-index number 1050

글로벌 토큰 (Global Token)

토큰 이름 (Token Name) 설명 (Description) 타입 (Type) 기본값 (Default Value)
borderRadius 기본 컴포넌트의 테두리 반경 number
borderRadiusLG LG 크기 테두리 반경이에요. Card, Modal 등 큰 테두리 반경을 가진 컴포넌트에 사용돼요. number
borderRadiusSM SM 크기 테두리 반경이에요. Button, Input, Select 등 작은 크기의 입력 컴포넌트에 사용돼요. number
boxShadowSecondary 요소의 2차 박스 섀도우 스타일을 제어해요. string
colorBgContainer 컨테이너 배경색이에요. 기본 버튼, 입력 상자 등. colorBgElevated와 혼동하지 마세요. string
colorBgContainerDisabled 비활성 상태에서 컨테이너의 배경색을 제어해요. string
colorBgElevated 팝업 레이어의 컨테이너 배경색이에요. 다크 모드에서는 이 토큰의 색이 colorBgContainer보다 약간 밝아요. 예: modal, pop-up, menu 등. string
colorBorder 기본 테두리 색이에요. 폼 구분선, 카드 구분선처럼 서로 다른 요소를 구분하는 데 사용돼요. string
colorBorderDisabled 비활성 상태의 요소 테두리 색을 제어해요. string
colorError 오류 Button, 오류 Result 컴포넌트 등 작업 실패의 시각적 요소를 나타내는 데 사용돼요. string
colorErrorAffix 오류 상태에서 폼 컨트롤 접두사/접미사 색을 제어해요. string
colorErrorBg 오류 상태의 배경색 string
colorErrorBgHover 오류 상태의 hover 배경색 string
colorErrorBorderHover 오류 상태의 hover 테두리 색 string
colorErrorText 오류 색에서 텍스트의 기본 상태 string
colorFillSecondary 2단계 fill 색으로 Rate, Skeleton 등 요소의 모양을 더 선명하게 나타낼 수 있어요. Table 등에서 3단계 fill 색의 Hover 상태로도 쓰여요. string
colorFillTertiary 3단계 fill 색으로 Slider, Segmented 등 요소의 모양을 나타내는 데 사용돼요. 강조 요구가 없다면 3단계 fill 색을 기본 fill로 쓰는 걸 권장해요. string
colorIcon 약한 동작이에요. allowClear나 Alert 닫기 버튼 같은 것. string
colorText W3C 표준을 따르는 기본 텍스트 색이에요. 가장 어두운 중성색이기도 해요. string
colorTextDisabled 비활성 상태의 텍스트 색을 제어해요. string
colorTextPlaceholder 플레이스홀더 텍스트 색을 제어해요. string
colorTextQuaternary 4단계 텍스트 색으로 가장 밝은 텍스트 색이에요. 폼 입력 힌트 텍스트, 비활성 색 텍스트 등. string
colorWarning Notification, Alert 등 경고를 나타내는 map 토큰에 사용돼요. Alert나 Input 같은 컨트롤 컴포넌트도 이 map 토큰을 사용해요. string
colorWarningAffix 경고 상태에서 폼 컨트롤 접두사/접미사 색을 제어해요. string
colorWarningBg 경고 상태의 배경색 string
colorWarningBgHover 경고 상태의 hover 배경색 string
colorWarningBorderHover 경고 상태의 hover 테두리 색 string
colorWarningText 경고 색에서 텍스트의 기본 상태 string
controlHeight Ant Design에서 버튼, 입력 상자 같은 기본 컨트롤의 높이 number
controlHeightLG LG 컴포넌트 높이 number
controlHeightSM SM 컴포넌트 높이 number
controlItemBgHover 컨트롤 컴포넌트 항목을 hover할 때 배경색을 제어해요. string
controlPaddingHorizontal 요소의 가로 패딩을 제어해요. number
fontFamily Ant Design의 글꼴은 시스템의 기본 인터페이스 글꼴을 우선시하고, 화면 표시에 적합한 대체 글꼴 라이브러리를 제공해 플랫폼과 브라우저에 따라 가독성을 유지하며 친근하고 안정적이며 전문적인 특성을 반영해요. string
fontSize 디자인 시스템에서 가장 널리 쓰이는 글자 크기로, 여기서 텍스트 그라데이션이 파생돼요. number
fontSizeIcon Select, Cascader 등의 동작 아이콘 글자 크기를 제어해요. 보통 fontSizeSM과 같아요. number
fontWeightStrong 제목 컴포넌트(h1, h2, h3 등)나 선택된 항목의 글자 굵기를 제어해요. number
lineHeight 텍스트의 줄 높이예요. number
lineHeightLG 큰 텍스트의 줄 높이예요. number
lineType 기본 컴포넌트의 테두리 스타일 string
lineWidth 기본 컴포넌트의 테두리 두께 number
marginXS 작은 크기의 요소 여백을 제어해요. number
motionDurationMid 동작 속도, 중간 속도예요. 중간 요소의 애니메이션 상호작용에 사용돼요. string
motionDurationSlow 동작 속도, 느린 속도예요. 큰 요소의 애니메이션 상호작용에 사용돼요. string
paddingXXS 요소의 아주 작은 패딩을 제어해요. number

더 알아보기 (Learn more)