组合框
Mantine Core 的独立组件,请不要替换为 JGuang UI/Base UI 的同名 API。
安装
npm install @mantine/core@9.7.1 @mantine/hooks@9.7.1
入口引入 @mantine/core/styles.css,并在根组件使用 MantineProvider;通过 forceColorScheme 同步深浅色主题。
使用与验证
在左侧选择组件,在预览中操作它的状态;使用“查看代码”检查真实源码。安装后执行类型检查和构建,再验证键盘焦点、手机布局与深浅色主题。
来源
https://github.com/mantinedev/mantine/tree/f0e2bc6d28b0f0b8aaefb5b859937bf77f539df7 MIT 许可证;不包括付费内容。下方提供上游英文参考内容,中文使用指南已适配;上游详细参考暂保留英文。
版本与兼容性
固定的 Mantine Core 9.7.1 要求 React 和 React DOM 满足 ^19.2.0,并配套 @mantine/hooks@9.7.1。此条件来自实际 npm 包的 peerDependencies。React 18 或其他不兼容版本不能强行安装;先向项目负责人说明升级影响并确认方案,不使用 --force/--legacy-peer-deps 掩盖冲突。入口的原生 Provider 开启 theme.respectReducedMotion,并用 forceColorScheme 控制深浅色。
适用场景
通过组合框 store 构建自定义选项选择。
本地示例使用的原生参数
以下参数由本地、经过类型检查的 JSX 示例提取,按所属组件区分。不是全部属性清单;完整签名和继承关系见下方固定源码参考。
| 组件参数 | 说明 |
|---|---|
Combobox.store | 原生组合框 store,不是 Base UI/Radix 原语。 |
Combobox.onOptionSubmit | 原生选项值提交回调。 |
Button.onClick | 本地点击处理,应按需替换为明确的业务操作。 |
Combobox.Option.value | 受控值,具体结构由原生类型定义。 |
交互与状态检查
当前场景:默认。切换支持的禁用、错误或只读场景后检查真实行为;无此原生状态的组件不会被强行添加统一属性。验证键盘焦点和退出方式,修改值后按原生回调签名连接业务状态。
业务接入边界
使用原生 useCombobox store,保留 Target/Dropdown/Options/Option 组合;onOptionSubmit 除了关闭列表,还应更新业务选择值。
宿主配置
先安装固定版本 @mantine/core 和 @mantine/hooks,在入口引入 @mantine/core/styles.css,并使用 MantineProvider。示例的 theme 和 variant 是 JGuang 编写的宿主参数,不是所有原生组件都接受的统一 API;通过 forceColorScheme 同步应用主题。源码面板中的原生实现仅供阅读,使用 npm 导出,不要单独复制内部实现。此批只覆盖 Core,不包括日期、图表和表单扩展包。上游详细说明保留英文,不宣称全文中文化。
可运行用法
以下代码使用资源原生 API;不是把其他设计系统的同名组件替换进来。
import { useState } from 'react';
import * as M from '@mantine/core';
import '@mantine/core/styles.css';
const fruit = ["Apple", "Banana", "Orange"];
function ComponentPreview({ variant = 'Default' }: { variant?: string }) {
const [value, setValue] = useState(35);
const [selection, setSelection] = useState<string | null>(null);
const combo = M.useCombobox();
return <M.Combobox store={combo} onOptionSubmit={(next) => { setSelection(next); combo.closeDropdown(); }}><M.Combobox.Target><M.Button onClick={() => combo.toggleDropdown()}>{selection ?? "Choose a fruit / 选择水果"}</M.Button></M.Combobox.Target><M.Combobox.Dropdown><M.Combobox.Options>{fruit.map((fruit) => <M.Combobox.Option key={fruit} value={fruit}>{fruit}</M.Combobox.Option>)}</M.Combobox.Options></M.Combobox.Dropdown></M.Combobox>;
}
export default function Example({ variant = 'Default', theme = 'light' }: { variant?: string; theme?: 'light' | 'dark' }) {
return <M.MantineProvider forceColorScheme={theme} theme={{ respectReducedMotion: true }}><ComponentPreview variant={variant} /></M.MantineProvider>;
}
可用状态与示例
默认
示例遵循 Mantine 原生 API。支持多状态的输入组件可将示例中的 variant 改为 Disabled、Invalid 或 Read only;这些只是示例标签,不是新增的组件属性。改造时保留组件要求的数据、受控值、store 和复合子组件结构。
Examples
This page contains only a small set of examples, as the full code of the demos is long. You can find all 50+ examples on a separate page.
Usage
Combobox provides a set of components and hooks to create custom select, multiselect or autocomplete components.
The component is very flexible – you have full control of the rendering and logic.
useCombobox hook
useCombobox hook provides a combobox store. The store contains the current state of the component
and handlers to update it. The created store must be passed to the store prop of Combobox:
import { Combobox, useCombobox } from '@mantine/core';
function Demo() {
const combobox = useCombobox();
return (
<Combobox store={combobox}>{/* Your implementation */}</Combobox>
);
}
useCombobox options
The useCombobox hook accepts an options object with the following properties:
interface UseComboboxOptions {
/** Default value for `dropdownOpened`, `false` by default */
defaultOpened?: boolean;
/** Controlled `dropdownOpened` state */
opened?: boolean;
/** Called when `dropdownOpened` state changes */
onOpenedChange?(opened: boolean): void;
/** Called when dropdown closes with event source: keyboard, mouse or unknown */
onDropdownClose?(eventSource: ComboboxDropdownEventSource): void;
/** Called when dropdown opens with event source: keyboard, mouse or unknown */
onDropdownOpen?(eventSource: ComboboxDropdownEventSource): void;
/** Determines whether arrow key presses should loop though items (first to last and last to first), `true` by default */
loop?: boolean;
/** `behavior` passed down to `element.scrollIntoView`, `'instant'` by default */
scrollBehavior?: ScrollBehavior;
}
You can import the UseComboboxOptions type from the @mantine/core package:
import type { UseComboboxOptions } from '@mantine/core';
Combobox store
The Combobox store is an object with the following properties:
interface ComboboxStore {
/** Current dropdown opened state */
dropdownOpened: boolean;
/** Opens dropdown */
openDropdown(eventSource?: 'keyboard' | 'mouse' | 'unknown'): void;
/** Closes dropdown */
closeDropdown(eventSource?: 'keyboard' | 'mouse' | 'unknown'): void;
/** Toggles dropdown opened state */
toggleDropdown(
eventSource?: 'keyboard' | 'mouse' | 'unknown'
): void;
/** Selected option index */
selectedOptionIndex: number;
/** Selects `Combobox.Option` by index */
selectOption(index: number): void;
/** Selects first `Combobox.Option` with `active` prop.
* If there are no such options, the function does nothing.
*/
selectActiveOption(): string | null;
/** Selects first `Combobox.Option` that is not disabled.
* If there are no such options, the function does nothing.
* */
selectFirstOption(): string | null;
/** Selects next `Combobox.Option` that is not disabled.
* If the current option is the last one, the function selects first option, if `loop` is true.
*/
selectNextOption(): string | null;
/** Selects previous `Combobox.Option` that is not disabled.
* If the current option is the first one, the function selects last option, if `loop` is true.
* */
selectPreviousOption(): string | null;
/** Resets selected option index to -1, removes `data-combobox-selected` from selected option */
resetSelectedOption(): void;
/** Triggers `onClick` event of selected option.
* If there is no selected option, the function does nothing.
*/
clickSelectedOption(): void;
/** Updates selected option index to currently selected or active option.
* The function is required to be used with searchable components to update selected option index
* when options list changes based on search query.
*/
updateSelectedOptionIndex(target?: 'active' | 'selected'): void;
/** List id, used for `aria-*` attributes */
listId: string | null;
/** Sets list id */
setListId(id: string): void;
/** Ref of `Combobox.Search` input */
searchRef: React.RefObject<HTMLInputElement | null>;
/** Moves focus to `Combobox.Search` input */
focusSearchInput(): void;
/** Ref of the target element */
targetRef: React.RefObject<HTMLElement | null>;
/** Moves focus to the target element */
focusTarget(): void;
}
You can import ComboboxStore type from @mantine/core package:
import type { ComboboxStore } from '@mantine/core';
useCombobox handlers
Combobox store handlers can be used to control Combobox state.
For example, to open the dropdown, call openDropdown handler:
import { Button, Combobox, useCombobox } from '@mantine/core';
function Demo() {
const combobox = useCombobox();
return (
<Combobox>
<Combobox.Target>
<Button onClick={() => combobox.openDropdown()}>
Open dropdown
</Button>
</Combobox.Target>
{/* Your implementation */}
</Combobox>
);
}
You can use store handlers in useCombobox options. For example, you can
call selectFirstOption when the dropdown is opened and resetSelectedOption
when it is closed:
import { Combobox, useCombobox } from '@mantine/core';
function Demo() {
const combobox = useCombobox({
onDropdownOpen: () => combobox.selectFirstOption(),
onDropdownClose: () => combobox.resetSelectedOption(),
});
return (
<Combobox store={combobox}>{/* Your implementation */}</Combobox>
);
}
Combobox.Target
Combobox.Target should be used as a wrapper for the target element or component.
Combobox.Target marks its child as a target for dropdown and sets aria-* attributes
and adds keyboard event listeners to it.
Combobox.Target requires a single child element or component. The child component
must accept ref and ...others props. You can use any Mantine component as a target without
any additional configuration, for example, Button, TextInput
or InputBase.
Example of using Combobox.Target with TextInput component:
Example of using Combobox.Target with Button component:
Split events and dropdown targets
In some cases, you might need to use different elements as an events target and as a dropdown.
Use Combobox.EventsTarget to add aria-* attributes and keyboard event handlers, and
Combobox.DropdownTarget to position the dropdown relative to the target.
You can have as many Combobox.EventsTarget as you need, but only one Combobox.DropdownTarget
per Combobox.
Example of using Combobox.EventsTarget and Combobox.DropdownTarget with PillsInput component
to create a searchable multiselect component:
Update selected option index
updateSelectedOptionIndex handler is required to be called when options list changes.
Usually, the options list changes when options are filtered based on the search query. In this case,
you need to call updateSelectedOptionIndex in onChange handler of the search input.
Example of using updateSelectedOptionIndex handler in searchable select component:
Search input
If you prefer search input inside the dropdown, use Combobox.Search component.
To focus the search input, call combobox.focusSearchInput, usually it is done
when the dropdown is opened. To prevent focus loss after the dropdown is closed,
call combobox.focusTarget:
Select first option
Use combobox.selectFirstOption function to select the first option. It is useful
if you want to select the first option when user searching for options in the list.
If there are no options available, it will do nothing.
Active option
Set active prop on Combobox.Option component to mark it as active.
By default, an active option does not have any styles, you can use data-combobox-active
data attribute to style it.
combobox.selectActiveOption function selects active option. Usually, it is called
when the dropdown is opened:
Options groups
Render Combobox.Option components inside Combobox.Group to create options group.
Combobox.Group label will be automatically hidden if the group does not have any
children.
Scrollable list
Set max-height style on either Combobox.Dropdown or Combobox.Options to make the
options list scrollable. You can use mah style prop to set
max-height.
Scrollable list with ScrollArea
You can also use ScrollArea or ScrollArea.Autosize components instead of native scrollbars:
Fit dropdown to viewport height
Set floatingHeight="viewport" to make the dropdown grow to fill the available vertical
space in the viewport. The dropdown size is recalculated automatically when the trigger
position changes. When the prop is set, the flip middleware is disabled – the dropdown
always opens in the configured direction and is constrained to the viewport edges instead
of flipping to the other side.
The dropdown exposes a --combobox-floating-options-max-height CSS variable equal to the
available height minus the dropdown padding. Pass it as mah to your ScrollArea.Autosize
(or use it on any element you want to scroll within the bounded dropdown):
Hide dropdown
Set hidden prop on Combobox.Dropdown to hide the dropdown. For example,
it can be useful when you want to show the dropdown only when there is at least
one option available:
Control dropdown opened state
To control the dropdown opened state, pass opened to useCombobox hook:
Popover props
Combobox supports most of Popover props. For example,
you can control dropdown position with position prop and disable Floating UI
middlewares with middlewares prop:
Without dropdown
You can use Combobox without dropdown. To do so, use Combobox.EventsTarget instead
of Combobox.Target:
Virtualization
useVirtualizedCombobox hook can be used to create comboboxes with virtualized options list.
Note that due to the nature of virtualization, the hook requires some additional configuration
compared to the useCombobox hook. In useVirtualizedCombobox hook all operations related to
option indexing do not rely on the actual DOM structure, instead, you need to preserve values in
React state and pass them to the hook.
useVirtualizedCombobox does not depend on any specific virtualization library.
The recommended option is @tanstack/react-virtual:
Example of implementation with react-virtuoso:
You can find more virtualization examples on the Combobox examples page.
Hook options:
export interface UseVirtualizedComboboxOptions {
/** Default value for `dropdownOpened`, `false` by default */
defaultOpened?: boolean;
/** Controlled `dropdownOpened` state */
opened?: boolean;
/** Called when `dropdownOpened` state changes */
onOpenedChange?: (opened: boolean) => void;
/** Called when dropdown closes */
onDropdownClose?: (eventSource: ComboboxDropdownEventSource) => void;
/** Called when dropdown opens */
onDropdownOpen?: (eventSource: ComboboxDropdownEventSource) => void;
/** Determines whether arrow key presses should loop though items (first to last and last to first), `true` by default */
loop?: boolean;
/** Function to determine whether the option is disabled */
isOptionDisabled?: (optionIndex: number) => boolean;
/** Total number of options in the virtualized list. Required for proper keyboard navigation and index calculations. */
totalOptionsCount: number;
/** Function that returns the id of the option at the given index. Required for setting aria attributes and element references. */
getOptionId: (index: number) => string | null;
/** Current selected option index. Must be controlled by parent component. */
selectedOptionIndex: number;
/** Callback to update the selected option index. Called when user navigates or selects options. */
setSelectedOptionIndex: (index: number) => void;
/** Currently active/highlighted option index. Used to determine which option to select when selectActiveOption is called. */
activeOptionIndex?: number;
/** Called when the selected option is submitted (e.g., via Enter key or clicking). Receives the selected option index. */
onSelectedOptionSubmit: (index: number) => void;
}
源码参数参考
以下实现签名和类型从本地固定版本源码生成。保留可选属性、原生继承类型、联合分支和 never 限制;不会将其他设计系统的 API 混入。forwardRef 实现中的 ref 属于 React 引用,不是新增业务属性。此区域是原始类型参考,不是上游说明的全文翻译。
Combobox(_props: ComboboxProps)
export type ComboboxStylesNames =
| 'options'
| 'dropdown'
| 'option'
| 'search'
| 'empty'
| 'footer'
| 'header'
| 'group'
| 'groupLabel';
export type ComboboxCSSVariables = {
options: '--combobox-option-fz' | '--combobox-option-padding';
dropdown: '--combobox-padding' | '--combobox-option-fz' | '--combobox-option-padding';
};
export interface ComboboxProps extends __PopoverProps, StylesApiProps<ComboboxFactory> {
__staticSelector?: string;
/** Combobox content */
children?: React.ReactNode;
/** Combobox store, can be used to control combobox state */
store?: ComboboxStore;
/** Called when item is selected with the `Enter` key or by clicking it */
onOptionSubmit?: (value: string, optionProps: ComboboxOptionProps) => void;
/** Controls items `font-size` and `padding` @default 'sm' */
size?: MantineSize | (string & {});
/** Controls `padding` of the dropdown @default 4 */
dropdownPadding?: React.CSSProperties['padding'];
/** Determines whether selection should be reset when option is hovered @default false */
resetSelectionOnOptionHover?: boolean;
/** Determines whether the `Combobox` value can be changed */
readOnly?: boolean;
/** If set to `'viewport'`, the dropdown grows to fill the available vertical space in the viewport. Disables the `flip` middleware. */
floatingHeight?: 'viewport';
}
export type ComboboxFactory = Factory<{
props: ComboboxProps;
ref: HTMLDivElement;
stylesNames: ComboboxStylesNames;
vars: ComboboxCSSVariables;
staticComponents: {
Target: typeof ComboboxTarget;
Dropdown: typeof ComboboxDropdown;
Options: typeof ComboboxOptions;
Option: typeof ComboboxOption;
Search: typeof ComboboxSearch;
Empty: typeof ComboboxEmpty;
Chevron: typeof ComboboxChevron;
Footer: typeof ComboboxFooter;
Header: typeof ComboboxHeader;
EventsTarget: typeof ComboboxEventsTarget;
DropdownTarget: typeof ComboboxDropdownTarget;
Group: typeof ComboboxGroup;
ClearButton: typeof ComboboxClearButton;
HiddenInput: typeof ComboboxHiddenInput;
};
}>;
原生与外部类型
以下外部类型仍由 React 或资源自身依赖定义,没有手工展开为虚构属性。实际可用类型以安装版本的 TypeScript 检查为准。
__PopoverProps
StylesApiProps<ComboboxFactory>
Factory<{
props: ComboboxProps;
ref: HTMLDivElement;
stylesNames: ComboboxStylesNames;
vars: ComboboxCSSVariables;
staticComponents: {
Target: typeof ComboboxTarget;
Dropdown: typeof ComboboxDropdown;
Options: typeof ComboboxOptions;
Option: typeof ComboboxOption;
Search: typeof ComboboxSearch;
Empty: typeof ComboboxEmpty;
Chevron: typeof ComboboxChevron;
Footer: typeof ComboboxFooter;
Header: typeof ComboboxHeader;
EventsTarget: typeof ComboboxEventsTarget;
DropdownTarget: typeof ComboboxDropdownTarget;
Group: typeof ComboboxGroup;
ClearButton: typeof ComboboxClearButton;
HiddenInput: typeof ComboboxHiddenInput;
};
}>
HTMLDivElement
React.ReactNode
ComboboxStore
ComboboxOptionProps
MantineSize
React.CSSProperties