멘션
멘션 (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 |