or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.mdmulti-select.mdomnibar.mdquery-list.mdselect.mdsuggest.mdutilities.md
tile.json

suggest.mddocs/

Typeahead Input

The Suggest component provides text input with autocomplete/typeahead suggestions, allowing users to type and select from filtered results.

Capabilities

Suggest Component

React component for text input with autocomplete suggestions.

/**
 * Suggest component for text input with autocomplete functionality
 * @template T - Type of items in the suggestion list
 */
class Suggest<T> extends React.Component<SuggestProps<T>, SuggestState<T>> {
  /** Generic factory method for type inference */
  static ofType<U>(): new (props: SuggestProps<U>) => Suggest<U>;
  
  /** Reference to the input element */
  inputElement: HTMLInputElement | null;
}

interface SuggestProps<T> extends ListItemsProps<T>, Omit<SelectPopoverProps, "popoverTargetProps"> {
  /** Function to convert selected item to display string in input */
  inputValueRenderer: (item: T) => string;
  /** Currently selected item */
  selectedItem?: T | null;
  /** Default selected item for uncontrolled usage */
  defaultSelectedItem?: T;
  /** Whether to close suggestions on item selection */
  closeOnSelect?: boolean;
  /** Whether the suggest is disabled */
  disabled?: boolean;
  /** Whether the suggest should fill its container */
  fill?: boolean;
  /** Props for the input element */
  inputProps?: Partial<Omit<InputGroupProps, "disabled" | "fill" | "value" | "onChange">>;
  /** Props for the dropdown menu container */
  menuProps?: React.HTMLAttributes<HTMLUListElement>;
  /** Whether to open suggestions on keydown */
  openOnKeyDown?: boolean;
  /** Whether to reset input value when suggestions close */
  resetOnClose?: boolean;
}

interface SuggestState<T> {
  /** Whether the suggestions dropdown is open */
  isOpen: boolean;
  /** Currently selected item */
  selectedItem: T | null;
}

Usage Examples:

import React, { useState } from "react";
import { Suggest, ItemRenderer } from "@blueprintjs/select";
import { MenuItem } from "@blueprintjs/core";

interface Country {
  name: string;
  code: string;
  continent: string;
}

const countries: Country[] = [
  { name: "United States", code: "US", continent: "North America" },
  { name: "Canada", code: "CA", continent: "North America" },
  { name: "United Kingdom", code: "GB", continent: "Europe" },
  { name: "Germany", code: "DE", continent: "Europe" },
  { name: "Japan", code: "JP", continent: "Asia" },
];

const renderCountry: ItemRenderer<Country> = (country, { handleClick, modifiers, query }) => (
  <MenuItem
    key={country.code}
    text={country.name}
    label={country.code}
    onClick={handleClick}
    active={modifiers.active}
    disabled={modifiers.disabled}
  />
);

const CountrySuggest = () => {
  const [selectedCountry, setSelectedCountry] = useState<Country | null>(null);

  return (
    <Suggest<Country>
      items={countries}
      itemRenderer={renderCountry}
      inputValueRenderer={(country) => country.name}
      selectedItem={selectedCountry}
      onItemSelect={setSelectedCountry}
      noResults={<MenuItem disabled text="No countries found." />}
      inputProps={{ placeholder: "Type to search countries..." }}
      closeOnSelect
    />
  );
};

// Uncontrolled usage with default value
const UncontrolledCountrySuggest = () => (
  <Suggest<Country>
    items={countries}
    itemRenderer={renderCountry}
    inputValueRenderer={(country) => country.name}
    defaultSelectedItem={countries[0]}
    onItemSelect={(country) => console.log("Selected:", country.name)}
    inputProps={{ placeholder: "Search countries..." }}
  />
);

Input Value Rendering

The inputValueRenderer prop is required and determines how selected items are displayed in the input field.

type InputValueRenderer<T> = (item: T) => string;

Usage Examples:

interface User {
  firstName: string;
  lastName: string;
  email: string;
  id: number;
}

const users: User[] = [
  { firstName: "John", lastName: "Doe", email: "john@example.com", id: 1 },
  { firstName: "Jane", lastName: "Smith", email: "jane@example.com", id: 2 },
];

// Display full name in input
const UserSuggestByName = () => (
  <Suggest<User>
    items={users}
    itemRenderer={(user, { handleClick, modifiers }) => (
      <MenuItem
        text={`${user.firstName} ${user.lastName}`}
        label={user.email}
        onClick={handleClick}
        active={modifiers.active}
      />
    )}
    inputValueRenderer={(user) => `${user.firstName} ${user.lastName}`}
    onItemSelect={(user) => console.log("Selected user:", user)}
    inputProps={{ placeholder: "Search by name..." }}
  />
);

// Display email in input
const UserSuggestByEmail = () => (
  <Suggest<User>
    items={users}
    itemRenderer={(user, { handleClick, modifiers }) => (
      <MenuItem
        text={user.email}
        label={`${user.firstName} ${user.lastName}`}
        onClick={handleClick}
        active={modifiers.active}
      />
    )}
    inputValueRenderer={(user) => user.email}
    onItemSelect={(user) => console.log("Selected user:", user)}
    inputProps={{ placeholder: "Search by email..." }}
  />
);

Advanced Filtering

Suggest supports custom filtering logic for sophisticated search behaviors.

// Inherited from ListItemsProps
type ItemPredicate<T> = (query: string, item: T, index?: number, exactMatch?: boolean) => boolean;
type ItemListPredicate<T> = (query: string, items: T[]) => T[];

Usage Examples:

// Multi-field search predicate
const userPredicate: ItemPredicate<User> = (query, user) => {
  const searchTerms = query.toLowerCase().split(" ");
  const searchableText = [
    user.firstName.toLowerCase(),
    user.lastName.toLowerCase(),
    user.email.toLowerCase(),
  ].join(" ");

  return searchTerms.every(term => searchableText.includes(term));
};

// Custom list predicate with scoring
const scoredUserPredicate: ItemListPredicate<User> = (query, users) => {
  if (!query) return users;

  const scored = users.map(user => {
    let score = 0;
    const q = query.toLowerCase();
    
    // Exact match bonus
    if (user.firstName.toLowerCase() === q) score += 100;
    if (user.lastName.toLowerCase() === q) score += 100;
    
    // Starts with bonus
    if (user.firstName.toLowerCase().startsWith(q)) score += 50;
    if (user.lastName.toLowerCase().startsWith(q)) score += 50;
    
    // Contains bonus
    if (user.firstName.toLowerCase().includes(q)) score += 20;
    if (user.lastName.toLowerCase().includes(q)) score += 20;
    if (user.email.toLowerCase().includes(q)) score += 10;
    
    return { user, score };
  })
  .filter(({ score }) => score > 0)
  .sort((a, b) => b.score - a.score)
  .map(({ user }) => user);

  return scored;
};

const AdvancedFilterSuggest = () => (
  <Suggest<User>
    items={users}
    itemRenderer={(user, { handleClick, modifiers }) => (
      <MenuItem
        text={`${user.firstName} ${user.lastName}`}
        label={user.email}
        onClick={handleClick}
        active={modifiers.active}
      />
    )}
    inputValueRenderer={(user) => `${user.firstName} ${user.lastName}`}
    itemListPredicate={scoredUserPredicate}
    onItemSelect={(user) => console.log("Selected:", user)}
    inputProps={{ placeholder: "Advanced search..." }}
  />
);

Input Customization

Suggest provides extensive input customization through Blueprint's InputGroup props.

// Subset of InputGroup props available to Suggest
interface InputGroupProps {
  /** Whether input should autofocus */
  autoFocus?: boolean;
  /** Input placeholder text */
  placeholder?: string;
  /** Whether input is disabled */
  disabled?: boolean;
  /** Whether input should fill container */
  fill?: boolean;
  /** Large size variant */
  large?: boolean;
  /** Small size variant */
  small?: boolean;
  /** Left icon */
  leftIcon?: IconName;
  /** Right element (icon or component) */
  rightElement?: React.ReactNode;
  /** Input type */
  type?: string;
  /** Additional CSS classes */
  className?: string;
  /** Callback when input value changes */
  onChange?: React.ChangeEventHandler<HTMLInputElement>;
  /** Callback when input receives focus */
  onFocus?: React.FocusEventHandler<HTMLInputElement>;
  /** Callback when input loses focus */
  onBlur?: React.FocusEventHandler<HTMLInputElement>;
}

Usage Examples:

import { Suggest } from "@blueprintjs/select";
import { Button, InputGroup } from "@blueprintjs/core";

const CustomInputSuggest = () => {
  const [value, setValue] = useState("");
  
  return (
    <Suggest<string>
      items={["Apple", "Banana", "Cherry", "Date"]}
      itemRenderer={(item, { handleClick, modifiers }) => (
        <div onClick={handleClick} style={{ padding: "8px" }}>
          {item}
        </div>
      )}
      inputValueRenderer={(item) => item}
      onItemSelect={(item) => setValue(item)}
      inputProps={{
        leftIcon: "search",
        placeholder: "Search fruits...",
        large: true,
        fill: true,
        rightElement: value ? (
          <Button
            icon="cross"
            minimal
            small
            onClick={() => setValue("")}
          />
        ) : undefined,
        value,
        onChange: (e) => setValue(e.target.value),
      }}
    />
  );
};

Controlled vs Uncontrolled

Suggest can be used in both controlled and uncontrolled modes.

Usage Examples:

// Controlled usage
const ControlledSuggest = () => {
  const [selectedCountry, setSelectedCountry] = useState<Country | null>(null);
  const [inputValue, setInputValue] = useState("");

  return (
    <Suggest<Country>
      items={countries}
      itemRenderer={renderCountry}
      inputValueRenderer={(country) => country.name}
      selectedItem={selectedCountry}
      onItemSelect={(country) => {
        setSelectedCountry(country);
        setInputValue(country.name);
      }}
      query={inputValue}
      onQueryChange={(query) => setInputValue(query)}
      inputProps={{ placeholder: "Controlled suggest..." }}
    />
  );
};

// Uncontrolled usage
const UncontrolledSuggest = () => (
  <Suggest<Country>
    items={countries}
    itemRenderer={renderCountry}
    inputValueRenderer={(country) => country.name}
    defaultSelectedItem={countries[0]}
    onItemSelect={(country) => console.log("Selected:", country.name)}
    inputProps={{ placeholder: "Uncontrolled suggest..." }}
  />
);

Keyboard Behavior

Suggest supports comprehensive keyboard navigation and control.

Usage Examples:

const KeyboardSuggest = () => (
  <Suggest<Country>
    items={countries}
    itemRenderer={renderCountry}
    inputValueRenderer={(country) => country.name}
    onItemSelect={(country) => console.log("Selected:", country.name)}
    openOnKeyDown  // Open suggestions when user starts typing
    closeOnSelect  // Close suggestions after selection
    resetOnClose   // Reset input value when suggestions close
    inputProps={{
      placeholder: "Type to search, use arrows to navigate, Enter to select...",
      onKeyDown: (e) => {
        // Custom keyboard handling
        if (e.key === "Escape") {
          e.currentTarget.blur();
        }
      },
    }}
  />
);