Components related to selecting items from a list
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
The MultiSelect component allows users to choose multiple items from a list, displaying selected items as removable tags.
React component for selecting multiple items with tag-based display.
/**
* MultiSelect component for choosing multiple items with tag display
* @template T - Type of items in the list
*/
class MultiSelect<T> extends React.Component<MultiSelectProps<T>, MultiSelectState> {
/** Generic factory method for type inference */
static ofType<U>(): new (props: MultiSelectProps<U>) => MultiSelect<U>;
/** Reference to the tag input element */
input: HTMLInputElement | null;
/** Reference to the internal QueryList component */
queryList: QueryList<T> | null;
}
interface MultiSelectProps<T> extends ListItemsProps<T>, SelectPopoverProps {
/** Array of currently selected items */
selectedItems: T[];
/** Function to render each selected item as a tag */
tagRenderer: (item: T) => React.ReactNode;
/** Custom render function for the trigger element. Providing this moves search functionality to the popover */
customTarget?: (selectedItems: T[], isOpen: boolean) => React.ReactNode;
/** Whether the multi-select is disabled */
disabled?: boolean;
/** Whether the multi-select should fill its container */
fill?: boolean;
/** Props for the dropdown menu container */
menuProps?: React.HTMLAttributes<HTMLUListElement>;
/** Callback when all items are cleared */
onClear?: () => void;
/** Callback when an item tag is removed */
onRemove?: (value: T, index: number) => void;
/** Whether to open dropdown on keydown in the input (ignored if customTarget is provided) */
openOnKeyDown?: boolean;
/** Placeholder text for the input */
placeholder?: string;
/** Props passed to the internal TagInput component with some limitations */
tagInputProps?: Partial<Omit<TagInputProps, "inputValue" | "onInputChange">>;
}
interface MultiSelectState {
/** Whether the dropdown is currently open */
isOpen: boolean;
}Usage Examples:
import React, { useState } from "react";
import { MultiSelect, ItemRenderer } from "@blueprintjs/select";
import { MenuItem, Tag } from "@blueprintjs/core";
interface Technology {
name: string;
category: string;
color: string;
}
const technologies: Technology[] = [
{ name: "React", category: "Frontend", color: "#61DAFB" },
{ name: "TypeScript", category: "Language", color: "#3178C6" },
{ name: "Node.js", category: "Backend", color: "#339933" },
{ name: "Python", category: "Language", color: "#3776AB" },
{ name: "PostgreSQL", category: "Database", color: "#336791" },
];
const renderTechnology: ItemRenderer<Technology> = (tech, { handleClick, modifiers }) => (
<MenuItem
key={tech.name}
text={tech.name}
label={tech.category}
onClick={handleClick}
active={modifiers.active}
disabled={modifiers.disabled}
/>
);
const renderTechTag = (tech: Technology) => (
<Tag style={{ backgroundColor: tech.color, color: "white" }}>
{tech.name}
</Tag>
);
const TechnologyMultiSelect = () => {
const [selectedTechs, setSelectedTechs] = useState<Technology[]>([]);
const handleItemSelect = (tech: Technology) => {
if (!selectedTechs.find(t => t.name === tech.name)) {
setSelectedTechs([...selectedTechs, tech]);
}
};
const handleRemove = (techToRemove: Technology, index: number) => {
setSelectedTechs(selectedTechs.filter((_, i) => i !== index));
};
return (
<MultiSelect<Technology>
items={technologies}
selectedItems={selectedTechs}
itemRenderer={renderTechnology}
tagRenderer={renderTechTag}
onItemSelect={handleItemSelect}
onRemove={handleRemove}
onClear={() => setSelectedTechs([])}
placeholder="Select technologies..."
noResults={<MenuItem disabled text="No technologies found." />}
resetOnSelect
/>
);
};
// With custom filtering to exclude selected items
const getFilteredTechnologies = (query: string, selectedTechs: Technology[]) => {
const selectedNames = new Set(selectedTechs.map(t => t.name));
return technologies
.filter(tech => !selectedNames.has(tech.name))
.filter(tech =>
tech.name.toLowerCase().includes(query.toLowerCase()) ||
tech.category.toLowerCase().includes(query.toLowerCase())
);
};
const FilteredTechnologyMultiSelect = () => {
const [selectedTechs, setSelectedTechs] = useState<Technology[]>([]);
return (
<MultiSelect<Technology>
items={technologies}
selectedItems={selectedTechs}
itemRenderer={renderTechnology}
tagRenderer={renderTechTag}
itemListPredicate={(query) => getFilteredTechnologies(query, selectedTechs)}
onItemSelect={(tech) => setSelectedTechs([...selectedTechs, tech])}
onRemove={(_, index) => setSelectedTechs(selectedTechs.filter((_, i) => i !== index))}
placeholder="Select technologies..."
/>
);
};MultiSelect provides extensive customization options for tag display and behavior.
// Tag renderer function type
type TagRenderer<T> = (item: T) => React.ReactNode;
// TagInput props (subset available to MultiSelect)
interface TagInputProps {
/** Whether tags can be added by typing */
addOnBlur?: boolean;
/** Whether tags can be added by pasting */
addOnPaste?: boolean;
/** Whether the input should fill its container */
fill?: boolean;
/** Props for the input element */
inputProps?: React.HTMLAttributes<HTMLInputElement>;
/** Whether the input is disabled */
disabled?: boolean;
/** Large size variant */
large?: boolean;
/** Callback when input loses focus */
onInputChange?: React.ChangeEventHandler<HTMLInputElement>;
/** Callback when a tag is removed */
onRemove?: (value: string, index: number) => void;
/** Placeholder text */
placeholder?: string;
/** Whether tags should have a remove button */
tagProps?: TagProps;
/** Array of tag values */
values: React.ReactNode[];
}Usage Examples:
import { MultiSelect } from "@blueprintjs/select";
import { Tag, Intent } from "@blueprintjs/core";
// Custom tag renderer with different intents
const renderPriorityTag = (item: { name: string; priority: "high" | "medium" | "low" }) => {
const intent = {
high: Intent.DANGER,
medium: Intent.WARNING,
low: Intent.SUCCESS,
}[item.priority];
return (
<Tag intent={intent} minimal>
{item.name}
</Tag>
);
};
// With custom TagInput props
const CustomTagInputMultiSelect = () => (
<MultiSelect<string>
items={["Apple", "Banana", "Cherry", "Date"]}
selectedItems={[]}
itemRenderer={(item, { handleClick, modifiers }) => (
<div onClick={handleClick}>{item}</div>
)}
tagRenderer={(item) => <Tag>{item}</Tag>}
onItemSelect={() => {}}
tagInputProps={{
large: true,
fill: true,
placeholder: "Type to search...",
tagProps: {
minimal: true,
intent: Intent.PRIMARY,
},
}}
/>
);MultiSelect supports creating new items from user input.
// From ListItemsProps
interface ListItemsProps<T> {
/** Function to create new items from query string */
createNewItemFromQuery?: (query: string) => T | T[];
/** Function to render the "create new item" option */
createNewItemRenderer?: (
query: string,
active: boolean,
handleClick: React.MouseEventHandler<HTMLElement>,
) => React.JSX.Element | undefined;
/** Where to position the create new item option */
createNewItemPosition?: "first" | "last";
}Usage Examples:
const CreatableMultiSelect = () => {
const [tags, setTags] = useState<string[]>(["existing-tag"]);
const [selectedTags, setSelectedTags] = useState<string[]>([]);
const createNewTag = (query: string): string => {
return query.trim();
};
const renderCreateTagOption = (query: string, active: boolean, handleClick: React.MouseEventHandler) => (
<MenuItem
icon="add"
text={`Create "${query}"`}
active={active}
onClick={handleClick}
shouldDismissPopover={false}
/>
);
return (
<MultiSelect<string>
items={tags}
selectedItems={selectedTags}
itemRenderer={(tag, { handleClick, modifiers }) => (
<MenuItem
text={tag}
onClick={handleClick}
active={modifiers.active}
/>
)}
tagRenderer={(tag) => <Tag>{tag}</Tag>}
onItemSelect={(tag) => {
if (!selectedTags.includes(tag)) {
setSelectedTags([...selectedTags, tag]);
}
}}
createNewItemFromQuery={createNewTag}
createNewItemRenderer={renderCreateTagOption}
createNewItemPosition="first"
noResults={<MenuItem disabled text="Type to create a new tag..." />}
/>
);
};MultiSelect allows complete customization of the input area display.
interface MultiSelectProps<T> {
/** Custom render function for the entire input area */
customTarget?: (selectedItems: T[], isOpen: boolean) => React.ReactNode;
}Usage Examples:
import { MultiSelect } from "@blueprintjs/select";
import { Button, Tag } from "@blueprintjs/core";
const CustomTargetMultiSelect = () => {
const [selectedItems, setSelectedItems] = useState<string[]>([]);
const customTarget = (items: string[], isOpen: boolean) => (
<Button
text={items.length > 0 ? `${items.length} items selected` : "Select items..."}
rightIcon={isOpen ? "caret-up" : "caret-down"}
active={isOpen}
style={{ minWidth: "200px", justifyContent: "space-between" }}
/>
);
return (
<MultiSelect<string>
items={["Option 1", "Option 2", "Option 3", "Option 4"]}
selectedItems={selectedItems}
itemRenderer={(item, { handleClick, modifiers }) => (
<div onClick={handleClick} style={{ padding: "8px" }}>
{item}
</div>
)}
tagRenderer={(item) => <Tag>{item}</Tag>}
onItemSelect={(item) => setSelectedItems([...selectedItems, item])}
customTarget={customTarget}
/>
);
};