@blueprintjs/select
Version:
Components related to selecting items from a list
192 lines (191 loc) • 8.48 kB
TypeScript
import * as React from "react";
import { AbstractComponent, type Props } from "@blueprintjs/core";
import { type CreateNewItem, type ListItemsProps } from "../../common";
export interface QueryListProps<T> extends ListItemsProps<T> {
/**
* Initial active item, useful if the parent component is controlling its selectedItem but
* not activeItem.
*/
initialActiveItem?: T;
/**
* Additional props to apply to the `Menu` that is created within the `QueryList`
*/
menuProps?: React.HTMLAttributes<HTMLUListElement>;
/**
* Callback invoked when user presses a key, after processing `QueryList`'s own key events
* (up/down to navigate active item). This callback is passed to `renderer` and (along with
* `onKeyUp`) can be attached to arbitrary content elements to support keyboard selection.
*/
onKeyDown?: React.KeyboardEventHandler<HTMLElement>;
/**
* Callback invoked when user releases a key, after processing `QueryList`'s own key events
* (enter to select active item). This callback is passed to `renderer` and (along with
* `onKeyDown`) can be attached to arbitrary content elements to support keyboard selection.
*/
onKeyUp?: React.KeyboardEventHandler<HTMLElement>;
/**
* Customize rendering of the component.
* Receives an object with props that should be applied to elements as necessary.
*/
renderer: (listProps: QueryListRendererProps<T>) => React.JSX.Element;
/**
* Whether the list is disabled.
*
* @default false
*/
disabled?: boolean;
}
/**
* An object describing how to render a `QueryList`.
* A `QueryList` `renderer` receives this object as its sole argument.
*/
export interface QueryListRendererProps<T>// Omit `createNewItem`, because it's used strictly for internal tracking.
extends Pick<QueryListState<T>, "activeItem" | "filteredItems" | "query">, Props {
/**
* Selection handler that should be invoked when a new item has been chosen,
* perhaps because the user clicked it.
*/
handleItemSelect: (item: T, event?: React.SyntheticEvent<HTMLElement>) => void;
/**
* Handler that should be invoked when the user pastes one or more values.
*
* This callback will use `itemPredicate` with `exactMatch=true` to find a
* subset of `items` exactly matching the pasted `values` provided, then it
* will invoke `onItemsPaste` with those found items. Each pasted value that
* does not exactly match an item will be ignored.
*
* If creating items is enabled (by providing both `createNewItemFromQuery`
* and `createNewItemRenderer`), then pasted values that do not exactly
* match an existing item will emit a new item as created via
* `createNewItemFromQuery`.
*
* If `itemPredicate` returns multiple matching items for a particular query
* in `queries`, then only the first matching item will be emitted.
*/
handlePaste: (queries: string[]) => void;
/**
* Keyboard handler for up/down arrow keys to shift the active item.
* Attach this handler to any element that should support this interaction.
*/
handleKeyDown: React.KeyboardEventHandler<HTMLElement>;
/**
* Keyboard handler for enter key to select the active item.
* Attach this handler to any element that should support this interaction.
*/
handleKeyUp: React.KeyboardEventHandler<HTMLElement>;
/**
* Change handler for query string. Attach this to an input element to allow
* `QueryList` to control the query.
*/
handleQueryChange: React.ChangeEventHandler<HTMLInputElement>;
/** Rendered elements returned from `itemListRenderer` prop. */
itemList: React.ReactNode;
}
/** Exported for testing, not part of public API */
export interface QueryListState<T> {
/** The currently focused item (for keyboard interactions). */
activeItem: T | CreateNewItem | null;
/**
* The item returned from `createNewItemFromQuery(this.state.query)`, cached
* to avoid continuous reinstantions within `isCreateItemRendered`, where
* this element will be used to hide the "Create Item" option if its value
* matches the current `query`.
*/
createNewItem: T | T[] | undefined;
/** The original `items` array filtered by `itemListPredicate` or `itemPredicate`. */
filteredItems: T[];
/** The current query string. */
query: string;
}
/**
* Query list component.
*
* @see https://blueprintjs.com/docs/#select/query-list
*/
export declare class QueryList<T> extends AbstractComponent<QueryListProps<T>, QueryListState<T>> {
static displayName: string;
static defaultProps: {
disabled: boolean;
resetOnQuery: boolean;
};
/** @deprecated no longer necessary now that the TypeScript parser supports type arguments on JSX element tags */
static ofType<U>(): new (props: QueryListProps<U>) => QueryList<U>;
private itemsParentRef?;
private itemRefs;
private refHandlers;
/**
* Flag indicating that we should check whether selected item is in viewport
* after rendering, typically because of keyboard change. Set to `true` when
* manipulating state in a way that may cause active item to scroll away.
*/
private shouldCheckActiveItemInViewport;
/**
* The item that we expect to be the next selected active item (based on click
* or key interactions). When scrollToActiveItem = false, used to detect if
* an unexpected external change to the active item has been made.
*/
private expectedNextActiveItem;
/**
* Flag which is set to true while in between an ENTER "keydown" event and its
* corresponding "keyup" event.
*
* When entering text via an IME (https://en.wikipedia.org/wiki/Input_method),
* the ENTER key is pressed to confirm the character(s) to be input from a list
* of options. The operating system intercepts the ENTER "keydown" event and
* prevents it from propagating to the application, but "keyup" is still
* fired, triggering a spurious event which this component does not expect.
*
* To work around this quirk, we keep track of "real" key presses by setting
* this flag in handleKeyDown.
*/
private isEnterKeyPressed;
constructor(props: QueryListProps<T>);
render(): React.JSX.Element;
componentDidUpdate(prevProps: QueryListProps<T>): void;
scrollActiveItemIntoView(): void;
setQuery(query: string, resetActiveItem?: boolean | undefined, props?: Readonly<QueryListProps<T>> & Readonly<{
children?: React.ReactNode;
}>): void;
setActiveItem(activeItem: T | CreateNewItem | null): void;
/** default `itemListRenderer` implementation */
private renderItemList;
/** wrapper around `itemRenderer` to inject props */
private renderItem;
private renderCreateItemMenuItem;
private getActiveElement;
private getActiveIndex;
private getItemsParentPadding;
private handleItemCreate;
private handleItemSelect;
private handlePaste;
private handleKeyDown;
private handleKeyUp;
private handleInputQueryChange;
/**
* Get the next enabled item, moving in the given direction from the start
* index. A `null` return value means no suitable item was found.
*
* @param direction amount to move in each iteration, typically +/-1
* @param startIndex item to start iteration
*/
private getNextActiveItem;
/**
* @param createNewItem Checks if this item would match the current query. Cannot check this.state.createNewItem
* every time since state may not have been updated yet.
*/
private isCreateItemRendered;
private isCreateItemFirst;
private canCreateItems;
private wouldCreatedItemMatchSomeExistingItem;
private maybeResetQuery;
}
/**
* Get the next enabled item, moving in the given direction from the start
* index. A `null` return value means no suitable item was found.
*
* @param items the list of items
* @param itemDisabled callback to determine if a given item is disabled
* @param direction amount to move in each iteration, typically +/-1
* @param startIndex which index to begin moving from
*/
export declare function getFirstEnabledItem<T>(items: T[], itemDisabled?: keyof T | ((item: T, index: number) => boolean), direction?: number, startIndex?: number): T | CreateNewItem | null;