@blueprintjs/select
Advanced tools
Comparing version 4.3.1 to 4.4.0
@@ -8,1 +8,2 @@ import * as Classes from "./classes"; | ||
export * from "./predicate"; | ||
export type { SelectPopoverProps } from "./selectPopoverProps"; |
import * as React from "react"; | ||
import { AbstractPureComponent2, TagInputProps } from "@blueprintjs/core"; | ||
import { Popover2, Popover2Props } from "@blueprintjs/popover2"; | ||
import { IListItemsProps } from "../../common"; | ||
import { IListItemsProps, SelectPopoverProps } from "../../common"; | ||
import { QueryList } from "../query-list/queryList"; | ||
export interface MultiSelect2Props<T> extends IListItemsProps<T> { | ||
export interface MultiSelect2Props<T> extends IListItemsProps<T>, SelectPopoverProps { | ||
/** | ||
* Whether the component is non-interactive. | ||
* If true, the list's item renderer will not be called. | ||
* | ||
* @default false | ||
*/ | ||
disabled?: boolean; | ||
/** | ||
* Whether the component should take up the full width of its container. | ||
@@ -13,2 +19,7 @@ * This overrides `popoverProps.fill` and `tagInputProps.fill`. | ||
/** | ||
* If provided, this component will render a "clear" button inside its TagInput. | ||
* Clicking that button will invoke this callback to clear all items from the current selection. | ||
*/ | ||
onClear?: () => void; | ||
/** | ||
* Callback invoked when an item is removed from the selection by | ||
@@ -42,15 +53,18 @@ * removing its tag in the TagInput. This is generally more useful than | ||
/** | ||
* Props to spread to `Popover2`. | ||
* | ||
* Note that `content` cannot be changed, but we do support attaching a ref to the Popover2 component | ||
* instance (sometimes useful to reposition the popover after updating `selectedItems` in reaction to | ||
* a change external to this component). | ||
* Props to add to the `div` that wraps the TagInput | ||
*/ | ||
popoverProps?: Partial<Omit<Popover2Props, "content"> & { | ||
ref: React.RefObject<Popover2<React.HTMLProps<HTMLDivElement>>>; | ||
}>; | ||
popoverTargetProps?: React.HTMLAttributes<HTMLDivElement>; | ||
/** Controlled selected values. */ | ||
selectedItems?: T[]; | ||
/** Props to spread to `TagInput`. Use `query` and `onQueryChange` to control the input. */ | ||
tagInputProps?: Partial<TagInputProps> & object; | ||
/** | ||
* Props to spread to `TagInput`. | ||
* If you wish to control the value of the input, use `query` and `onQueryChange` instead. | ||
* | ||
* Notes for `tagInputProps.rightElement`: | ||
* - you are responsible for disabling any elements you may render here when the overall | ||
* `MultiSelect2` is disabled. | ||
* - if the `onClear` prop is defined, this element will override/replace the default rightElement, | ||
* which is a "clear" button that removes all items from the current selection. | ||
*/ | ||
tagInputProps?: Partial<TagInputProps>; | ||
/** Custom renderer to transform an item into tag content. */ | ||
@@ -64,3 +78,5 @@ tagRenderer: (item: T) => React.ReactNode; | ||
static displayName: string; | ||
private listboxId; | ||
static defaultProps: { | ||
disabled: boolean; | ||
fill: boolean; | ||
@@ -78,2 +94,3 @@ placeholder: string; | ||
private renderQueryList; | ||
private getPopoverTargetRenderer; | ||
private handleItemSelect; | ||
@@ -84,4 +101,6 @@ private handleQueryChange; | ||
private handleTagRemove; | ||
private getTagInputAddHandler; | ||
private getTagInputKeyDownHandler; | ||
private getTagInputKeyUpHandler; | ||
private handleClearButtonClick; | ||
} |
@@ -31,2 +31,3 @@ "use strict"; | ||
_this = _super.apply(this, arguments) || this; | ||
_this.listboxId = core_1.Utils.uniqueId("listbox"); | ||
_this.state = { | ||
@@ -44,23 +45,43 @@ isOpen: (_this.props.popoverProps && _this.props.popoverProps.isOpen) || false, | ||
_this.renderQueryList = function (listProps) { | ||
var _a; | ||
var _b = _this.props, fill = _b.fill, _c = _b.tagInputProps, tagInputProps = _c === void 0 ? {} : _c, _d = _b.popoverProps, popoverProps = _d === void 0 ? {} : _d, _e = _b.selectedItems, selectedItems = _e === void 0 ? [] : _e, placeholder = _b.placeholder; | ||
var handlePaste = listProps.handlePaste, handleKeyDown = listProps.handleKeyDown, handleKeyUp = listProps.handleKeyUp; | ||
if (fill) { | ||
popoverProps.fill = true; | ||
tagInputProps.fill = true; | ||
} | ||
// add our own inputProps.className so that we can reference it in event handlers | ||
var inputProps = tslib_1.__assign(tslib_1.__assign({}, tagInputProps.inputProps), { className: (0, classnames_1.default)((_a = tagInputProps.inputProps) === null || _a === void 0 ? void 0 : _a.className, common_1.Classes.MULTISELECT_TAG_INPUT_INPUT) }); | ||
var handleTagInputAdd = function (values, method) { | ||
if (method === "paste") { | ||
handlePaste(values); | ||
var _a = _this.props, disabled = _a.disabled, _b = _a.popoverContentProps, popoverContentProps = _b === void 0 ? {} : _b, _c = _a.popoverProps, popoverProps = _c === void 0 ? {} : _c; | ||
var handleKeyDown = listProps.handleKeyDown, handleKeyUp = listProps.handleKeyUp; | ||
var popoverRef = _this.props.popoverRef === undefined | ||
? _this.refHandlers.popover | ||
: (0, core_1.mergeRefs)(_this.refHandlers.popover, _this.props.popoverRef); | ||
// N.B. no need to set `popoverProps.fill` since that is unused with the `renderTarget` API | ||
return (React.createElement(popover2_1.Popover2, tslib_1.__assign({ autoFocus: false, canEscapeKeyClose: true, disabled: disabled, enforceFocus: false, isOpen: _this.state.isOpen, placement: popoverProps.position || popoverProps.placement ? undefined : "bottom-start" }, popoverProps, { className: (0, classnames_1.default)(listProps.className, popoverProps.className), content: React.createElement("div", tslib_1.__assign({}, popoverContentProps, { onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }), listProps.itemList), interactionKind: "click", onInteraction: _this.handlePopoverInteraction, onOpened: _this.handlePopoverOpened, popoverClassName: (0, classnames_1.default)(common_1.Classes.MULTISELECT_POPOVER, popoverProps.popoverClassName), popupKind: popover2_1.PopupKind.LISTBOX, ref: popoverRef, renderTarget: _this.getPopoverTargetRenderer(listProps, _this.state.isOpen) }))); | ||
}; | ||
// We use the renderTarget API to flatten the rendered DOM and make it easier to implement features like | ||
// the "fill" prop. Note that we must take `isOpen` as an argument to force this render function to be called | ||
// again after that state changes. | ||
_this.getPopoverTargetRenderer = function (listProps, isOpen) { | ||
// N.B. pull out `isOpen` so that it's not forwarded to the DOM, but remember not to use it directly | ||
// since it may be stale (`renderTarget` is not re-invoked on this.state changes). | ||
// eslint-disable-next-line react/display-name | ||
return function (_a) { | ||
var _b; | ||
var _c; | ||
var _isOpen = _a.isOpen, ref = _a.ref, targetProps = tslib_1.__rest(_a, ["isOpen", "ref"]); | ||
var _d = _this.props, disabled = _d.disabled, fill = _d.fill, onClear = _d.onClear, placeholder = _d.placeholder, _e = _d.popoverTargetProps, popoverTargetProps = _e === void 0 ? {} : _e, _f = _d.selectedItems, selectedItems = _f === void 0 ? [] : _f, _g = _d.tagInputProps, tagInputProps = _g === void 0 ? {} : _g; | ||
var handleKeyDown = listProps.handleKeyDown, handleKeyUp = listProps.handleKeyUp; | ||
if (disabled) { | ||
tagInputProps.disabled = true; | ||
} | ||
if (fill) { | ||
tagInputProps.fill = true; | ||
} | ||
// add our own inputProps.className so that we can reference it in event handlers | ||
var inputProps = tslib_1.__assign(tslib_1.__assign({}, tagInputProps.inputProps), { className: (0, classnames_1.default)((_c = tagInputProps.inputProps) === null || _c === void 0 ? void 0 : _c.className, common_1.Classes.MULTISELECT_TAG_INPUT_INPUT) }); | ||
var maybeClearButton = onClear !== undefined && selectedItems.length > 0 ? (React.createElement(core_1.Button, { disabled: disabled, icon: "cross", minimal: true, onClick: _this.handleClearButtonClick })) : undefined; | ||
return (React.createElement("div", tslib_1.__assign({ "aria-autocomplete": "list", "aria-controls": _this.listboxId }, popoverTargetProps, targetProps, { "aria-expanded": isOpen, | ||
// Note that we must set FILL here in addition to TagInput to get the wrapper element to full width | ||
className: (0, classnames_1.default)(targetProps.className, popoverTargetProps.className, (_b = {}, | ||
_b[core_1.Classes.FILL] = fill, | ||
_b)), | ||
// Normally, Popover2 would also need to attach its own `onKeyDown` handler via `targetProps`, | ||
// but in our case we fully manage that interaction and listen for key events to open/close | ||
// the popover, so we elide it from the DOM. | ||
onKeyDown: _this.getTagInputKeyDownHandler(handleKeyDown), onKeyUp: _this.getTagInputKeyUpHandler(handleKeyUp), ref: ref, role: "combobox" }), | ||
React.createElement(core_1.TagInput, tslib_1.__assign({ placeholder: placeholder, rightElement: maybeClearButton }, tagInputProps, { className: (0, classnames_1.default)(common_1.Classes.MULTISELECT, tagInputProps.className), inputRef: _this.refHandlers.input, inputProps: inputProps, inputValue: listProps.query, onAdd: _this.getTagInputAddHandler(listProps), onInputChange: listProps.handleQueryChange, onRemove: _this.handleTagRemove, values: selectedItems.map(_this.props.tagRenderer) })))); | ||
}; | ||
return (React.createElement(popover2_1.Popover2, tslib_1.__assign({ autoFocus: false, canEscapeKeyClose: true, enforceFocus: false, isOpen: _this.state.isOpen, placement: "bottom-start" }, popoverProps, { className: (0, classnames_1.default)(listProps.className, popoverProps.className), content: React.createElement("div", { onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }, listProps.itemList), interactionKind: "click", onInteraction: _this.handlePopoverInteraction, onOpened: _this.handlePopoverOpened, popoverClassName: (0, classnames_1.default)(common_1.Classes.MULTISELECT_POPOVER, popoverProps.popoverClassName), ref: popoverProps.ref === undefined | ||
? _this.refHandlers.popover | ||
: (0, core_1.mergeRefs)(_this.refHandlers.popover, popoverProps.ref) }), | ||
React.createElement("div", { onKeyDown: _this.getTagInputKeyDownHandler(handleKeyDown), onKeyUp: _this.getTagInputKeyUpHandler(handleKeyUp) }, | ||
React.createElement(core_1.TagInput, tslib_1.__assign({ placeholder: placeholder }, tagInputProps, { className: (0, classnames_1.default)(common_1.Classes.MULTISELECT, tagInputProps.className), inputRef: _this.refHandlers.input, inputProps: inputProps, inputValue: listProps.query, | ||
/* eslint-disable-next-line react/jsx-no-bind */ | ||
onAdd: handleTagInputAdd, onInputChange: listProps.handleQueryChange, onRemove: _this.handleTagRemove, values: selectedItems.map(_this.props.tagRenderer) }))))); | ||
}; | ||
@@ -112,2 +133,7 @@ _this.handleItemSelect = function (item, evt) { | ||
}; | ||
_this.getTagInputAddHandler = function (listProps) { return function (values, method) { | ||
if (method === "paste") { | ||
listProps.handlePaste(values); | ||
} | ||
}; }; | ||
_this.getTagInputKeyDownHandler = function (handleQueryListKeyDown) { | ||
@@ -145,2 +171,7 @@ return function (e) { | ||
}; | ||
_this.handleClearButtonClick = function () { | ||
var _a, _b, _c; | ||
(_b = (_a = _this.props).onClear) === null || _b === void 0 ? void 0 : _b.call(_a); | ||
(_c = _this.refHandlers.popover.current) === null || _c === void 0 ? void 0 : _c.reposition(); // reposition when size of input changes | ||
}; | ||
return _this; | ||
@@ -158,2 +189,6 @@ } | ||
} | ||
if ((prevProps.onClear === undefined && this.props.onClear !== undefined) || | ||
(prevProps.onClear !== undefined && this.props.onClear === undefined)) { | ||
this.forceUpdate(); | ||
} | ||
}; | ||
@@ -163,6 +198,7 @@ MultiSelect2.prototype.render = function () { | ||
var _a = this.props, openOnKeyDown = _a.openOnKeyDown, popoverProps = _a.popoverProps, tagInputProps = _a.tagInputProps, restProps = tslib_1.__rest(_a, ["openOnKeyDown", "popoverProps", "tagInputProps"]); | ||
return (React.createElement(this.TypedQueryList, tslib_1.__assign({}, restProps, { onItemSelect: this.handleItemSelect, onQueryChange: this.handleQueryChange, ref: this.refHandlers.queryList, renderer: this.renderQueryList }))); | ||
return (React.createElement(this.TypedQueryList, tslib_1.__assign({}, restProps, { menuProps: { "aria-multiselectable": true, id: this.listboxId }, onItemSelect: this.handleItemSelect, onQueryChange: this.handleQueryChange, ref: this.refHandlers.queryList, renderer: this.renderQueryList }))); | ||
}; | ||
MultiSelect2.displayName = "".concat(core_1.DISPLAYNAME_PREFIX, ".MultiSelect2"); | ||
MultiSelect2.defaultProps = { | ||
disabled: false, | ||
fill: false, | ||
@@ -169,0 +205,0 @@ placeholder: "Search...", |
@@ -13,2 +13,6 @@ import * as React from "react"; | ||
/** | ||
* 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 | ||
@@ -15,0 +19,0 @@ * (up/down to navigate active item). This callback is passed to `renderer` and (along with |
@@ -60,3 +60,3 @@ "use strict"; | ||
_this.renderItemList = function (listProps) { | ||
var _a = _this.props, initialContent = _a.initialContent, noResults = _a.noResults; | ||
var _a = _this.props, initialContent = _a.initialContent, noResults = _a.noResults, menuProps = _a.menuProps; | ||
// omit noResults if createNewItemFromQuery and createNewItemRenderer are both supplied, and query is not empty | ||
@@ -70,3 +70,3 @@ var createItemView = listProps.renderCreateItem(); | ||
var createFirst = _this.isCreateItemFirst(); | ||
return (React.createElement(core_1.Menu, { ulRef: listProps.itemsParentRef }, | ||
return (React.createElement(core_1.Menu, tslib_1.__assign({ role: "listbox" }, menuProps, { ulRef: listProps.itemsParentRef }), | ||
createFirst && createItemView, | ||
@@ -73,0 +73,0 @@ menuContent, |
import * as React from "react"; | ||
import { AbstractPureComponent2, InputGroupProps2 } from "@blueprintjs/core"; | ||
import { Popover2Props } from "@blueprintjs/popover2"; | ||
import { IListItemsProps } from "../../common"; | ||
export interface Select2Props<T> extends IListItemsProps<T> { | ||
import { IListItemsProps, SelectPopoverProps } from "../../common"; | ||
export interface Select2Props<T> extends IListItemsProps<T>, SelectPopoverProps { | ||
/** | ||
* Element which triggers the select popover. In most cases, you should display | ||
* the name or label of the curently selected item here. | ||
*/ | ||
children?: React.ReactNode; | ||
/** | ||
* Whether the component is non-interactive. | ||
* If true, the list's item renderer will not be called. | ||
* Note that you'll also need to disable the component's children, if appropriate. | ||
* | ||
* @default false | ||
*/ | ||
disabled?: boolean; | ||
/** | ||
* Whether the component should take up the full width of its container. | ||
@@ -21,10 +32,2 @@ * This overrides `popoverProps.fill`. You also have to ensure that the child | ||
/** | ||
* Whether the component is non-interactive. | ||
* If true, the list's item renderer will not be called. | ||
* Note that you'll also need to disable the component's children, if appropriate. | ||
* | ||
* @default false | ||
*/ | ||
disabled?: boolean; | ||
/** | ||
* Props to spread to the query `InputGroup`. Use `query` and | ||
@@ -35,5 +38,7 @@ * `onQueryChange` instead of `inputProps.value` and `inputProps.onChange` | ||
inputProps?: InputGroupProps2; | ||
/** Props to spread to `Popover2`. Note that `content` cannot be changed. */ | ||
popoverProps?: Partial<Omit<Popover2Props, "content">>; | ||
/** | ||
* Props to add to the popover target wrapper element. | ||
*/ | ||
popoverTargetProps?: React.HTMLAttributes<HTMLDivElement>; | ||
/** | ||
* Whether the active item should be reset to the first matching item _when | ||
@@ -53,4 +58,4 @@ * the popover closes_. The query will also be reset to the empty string. | ||
state: Select2State; | ||
inputElement: HTMLInputElement | null; | ||
private TypedQueryList; | ||
inputElement: HTMLInputElement | null; | ||
private queryList; | ||
@@ -60,6 +65,11 @@ private previousFocusedElement; | ||
private handleQueryListRef; | ||
private listboxId; | ||
render(): JSX.Element; | ||
componentDidUpdate(prevProps: Select2Props<T>, prevState: Select2State): void; | ||
private renderQueryList; | ||
private getPopoverTargetRenderer; | ||
private maybeRenderClearButton; | ||
/** | ||
* Target wrapper element "keydown" handler while the popover is closed. | ||
*/ | ||
private handleTargetKeyDown; | ||
@@ -66,0 +76,0 @@ private handleItemSelect; |
@@ -34,24 +34,50 @@ "use strict"; | ||
_this.state = { isOpen: false }; | ||
_this.inputElement = null; | ||
_this.TypedQueryList = queryList_1.QueryList.ofType(); | ||
_this.inputElement = null; | ||
_this.queryList = null; | ||
_this.handleInputRef = (0, core_1.refHandler)(_this, "inputElement", (_a = _this.props.inputProps) === null || _a === void 0 ? void 0 : _a.inputRef); | ||
_this.handleQueryListRef = function (ref) { return (_this.queryList = ref); }; | ||
_this.listboxId = core_1.Utils.uniqueId("listbox"); | ||
_this.renderQueryList = function (listProps) { | ||
// not using defaultProps cuz they're hard to type with generics (can't use <T> on static members) | ||
var _a = _this.props, fill = _a.fill, _b = _a.filterable, filterable = _b === void 0 ? true : _b, _c = _a.disabled, disabled = _c === void 0 ? false : _c, _d = _a.inputProps, inputProps = _d === void 0 ? {} : _d, _e = _a.popoverProps, popoverProps = _e === void 0 ? {} : _e; | ||
var _a = _this.props, fill = _a.fill, _b = _a.filterable, filterable = _b === void 0 ? true : _b, _c = _a.disabled, disabled = _c === void 0 ? false : _c, _d = _a.inputProps, inputProps = _d === void 0 ? {} : _d, _e = _a.popoverContentProps, popoverContentProps = _e === void 0 ? {} : _e, _f = _a.popoverProps, popoverProps = _f === void 0 ? {} : _f, popoverRef = _a.popoverRef; | ||
if (fill) { | ||
popoverProps.fill = true; | ||
} | ||
var input = (React.createElement(core_1.InputGroup, tslib_1.__assign({ leftIcon: "search", placeholder: "Filter...", rightElement: _this.maybeRenderClearButton(listProps.query) }, inputProps, { inputRef: _this.handleInputRef, onChange: listProps.handleQueryChange, value: listProps.query }))); | ||
var input = (React.createElement(core_1.InputGroup, tslib_1.__assign({ "aria-autocomplete": "list", leftIcon: "search", placeholder: "Filter...", rightElement: _this.maybeRenderClearButton(listProps.query) }, inputProps, { inputRef: _this.handleInputRef, onChange: listProps.handleQueryChange, value: listProps.query }))); | ||
var handleKeyDown = listProps.handleKeyDown, handleKeyUp = listProps.handleKeyUp; | ||
return (React.createElement(popover2_1.Popover2, tslib_1.__assign({ autoFocus: false, enforceFocus: false, isOpen: _this.state.isOpen, disabled: disabled, position: core_1.Position.BOTTOM_LEFT }, popoverProps, { className: (0, classnames_1.default)(listProps.className, popoverProps.className), content: React.createElement("div", { onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }, | ||
return (React.createElement(popover2_1.Popover2, tslib_1.__assign({ autoFocus: false, enforceFocus: false, isOpen: _this.state.isOpen, disabled: disabled, position: core_1.Position.BOTTOM_LEFT }, popoverProps, { className: (0, classnames_1.default)(listProps.className, popoverProps.className), content: React.createElement("div", tslib_1.__assign({}, popoverContentProps, { onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }), | ||
filterable ? input : undefined, | ||
listProps.itemList), onInteraction: _this.handlePopoverInteraction, popoverClassName: (0, classnames_1.default)(common_1.Classes.SELECT_POPOVER, popoverProps.popoverClassName), onOpening: _this.handlePopoverOpening, onOpened: _this.handlePopoverOpened, onClosing: _this.handlePopoverClosing }), | ||
React.createElement("div", { onKeyDown: _this.state.isOpen ? handleKeyDown : _this.handleTargetKeyDown, onKeyUp: _this.state.isOpen ? handleKeyUp : undefined }, _this.props.children))); | ||
listProps.itemList), onClosing: _this.handlePopoverClosing, onInteraction: _this.handlePopoverInteraction, onOpened: _this.handlePopoverOpened, onOpening: _this.handlePopoverOpening, popoverClassName: (0, classnames_1.default)(common_1.Classes.SELECT_POPOVER, popoverProps.popoverClassName), popupKind: popover2_1.PopupKind.LISTBOX, ref: popoverRef, renderTarget: _this.getPopoverTargetRenderer(listProps, _this.state.isOpen) }))); | ||
}; | ||
// We use the renderTarget API to flatten the rendered DOM and make it easier to implement features like | ||
// the "fill" prop. Note that we must take `isOpen` as an argument to force this render function to be called | ||
// again after that state changes. | ||
_this.getPopoverTargetRenderer = function (listProps, isOpen) { | ||
// N.B. pull out `isOpen` so that it's not forwarded to the DOM, but remember not to use it directly | ||
// since it may be stale (`renderTarget` is not re-invoked on this.state changes). | ||
// eslint-disable-next-line react/display-name | ||
return function (_a) { | ||
var _b; | ||
var _isOpen = _a.isOpen, ref = _a.ref, targetProps = tslib_1.__rest(_a, ["isOpen", "ref"]); | ||
var popoverTargetProps = _this.props.popoverTargetProps; | ||
var handleKeyDown = listProps.handleKeyDown, handleKeyUp = listProps.handleKeyUp; | ||
return (React.createElement("div", tslib_1.__assign({ "aria-controls": _this.listboxId }, popoverTargetProps, targetProps, { "aria-expanded": isOpen, | ||
// Note that we must set FILL here in addition to children to get the wrapper element to full width | ||
className: (0, classnames_1.default)(targetProps.className, popoverTargetProps === null || popoverTargetProps === void 0 ? void 0 : popoverTargetProps.className, (_b = {}, | ||
_b[core_1.Classes.FILL] = _this.props.fill, | ||
_b)), | ||
// Normally, Popover2 would also need to attach its own `onKeyDown` handler via `targetProps`, | ||
// but in our case we fully manage that interaction and listen for key events to open/close | ||
// the popover, so we elide it from the DOM. | ||
onKeyDown: isOpen ? handleKeyDown : _this.handleTargetKeyDown, onKeyUp: isOpen ? handleKeyUp : undefined, ref: ref, role: "combobox" }), _this.props.children)); | ||
}; | ||
}; | ||
/** | ||
* Target wrapper element "keydown" handler while the popover is closed. | ||
*/ | ||
_this.handleTargetKeyDown = function (event) { | ||
// open popover when arrow key pressed on target while closed | ||
// HACKHACK: https://github.com/palantir/blueprint/issues/4165 | ||
// eslint-disable-next-line deprecation/deprecation | ||
/* eslint-disable deprecation/deprecation */ | ||
if (event.which === core_1.Keys.ARROW_UP || event.which === core_1.Keys.ARROW_DOWN) { | ||
@@ -61,2 +87,6 @@ event.preventDefault(); | ||
} | ||
else if (core_1.Keys.isKeyboardClick(event.keyCode)) { | ||
_this.setState({ isOpen: true }); | ||
} | ||
/* eslint-enable deprecation/deprecation */ | ||
}; | ||
@@ -120,3 +150,3 @@ _this.handleItemSelect = function (item, event) { | ||
var _a = this.props, filterable = _a.filterable, inputProps = _a.inputProps, popoverProps = _a.popoverProps, restProps = tslib_1.__rest(_a, ["filterable", "inputProps", "popoverProps"]); | ||
return (React.createElement(this.TypedQueryList, tslib_1.__assign({}, restProps, { onItemSelect: this.handleItemSelect, ref: this.handleQueryListRef, renderer: this.renderQueryList }))); | ||
return (React.createElement(this.TypedQueryList, tslib_1.__assign({}, restProps, { menuProps: { id: this.listboxId }, onItemSelect: this.handleItemSelect, ref: this.handleQueryListRef, renderer: this.renderQueryList }))); | ||
}; | ||
@@ -123,0 +153,0 @@ Select2.prototype.componentDidUpdate = function (prevProps, prevState) { |
/// <reference types="react" /> | ||
import { AbstractPureComponent2, InputGroupProps2 } from "@blueprintjs/core"; | ||
import { Popover2Props } from "@blueprintjs/popover2"; | ||
import { IListItemsProps } from "../../common"; | ||
export interface Suggest2Props<T> extends IListItemsProps<T> { | ||
import { IListItemsProps, SelectPopoverProps } from "../../common"; | ||
export interface Suggest2Props<T> extends IListItemsProps<T>, SelectPopoverProps { | ||
/** | ||
@@ -48,4 +47,2 @@ * Whether the popover should close after selecting an item. | ||
openOnKeyDown?: boolean; | ||
/** Props to spread to `Popover2`. Note that `content` cannot be changed. */ | ||
popoverProps?: Partial<Omit<Popover2Props, "content">>; | ||
/** | ||
@@ -73,5 +70,7 @@ * Whether the active item should be reset to the first matching item _when | ||
private handleQueryListRef; | ||
private listboxId; | ||
render(): JSX.Element; | ||
componentDidUpdate(prevProps: Suggest2Props<T>, prevState: Suggest2State<T>): void; | ||
private renderQueryList; | ||
private getPopoverTargetRenderer; | ||
private selectText; | ||
@@ -78,0 +77,0 @@ private handleInputFocus; |
@@ -42,22 +42,34 @@ "use strict"; | ||
_this.handleQueryListRef = function (ref) { return (_this.queryList = ref); }; | ||
_this.listboxId = core_1.Utils.uniqueId("listbox"); | ||
_this.renderQueryList = function (listProps) { | ||
var _a = _this.props, fill = _a.fill, _b = _a.inputProps, inputProps = _b === void 0 ? {} : _b, _c = _a.popoverProps, popoverProps = _c === void 0 ? {} : _c; | ||
var _d = _this.state, isOpen = _d.isOpen, selectedItem = _d.selectedItem; | ||
var _a = _this.props, _b = _a.popoverContentProps, popoverContentProps = _b === void 0 ? {} : _b, _c = _a.popoverProps, popoverProps = _c === void 0 ? {} : _c, popoverRef = _a.popoverRef; | ||
var isOpen = _this.state.isOpen; | ||
var handleKeyDown = listProps.handleKeyDown, handleKeyUp = listProps.handleKeyUp; | ||
var _e = inputProps.autoComplete, autoComplete = _e === void 0 ? "off" : _e, _f = inputProps.placeholder, placeholder = _f === void 0 ? "Search..." : _f; | ||
var selectedItemText = selectedItem ? _this.props.inputValueRenderer(selectedItem) : ""; | ||
// placeholder shows selected item while open. | ||
var inputPlaceholder = isOpen && selectedItemText ? selectedItemText : placeholder; | ||
// value shows query when open, and query remains when closed if nothing is selected. | ||
// if resetOnClose is enabled, then hide query when not open. (see handlePopoverOpening) | ||
var inputValue = isOpen | ||
? listProps.query | ||
: selectedItemText || (_this.props.resetOnClose ? "" : listProps.query); | ||
if (fill) { | ||
popoverProps.fill = true; | ||
inputProps.fill = true; | ||
} | ||
return (React.createElement(popover2_1.Popover2, tslib_1.__assign({ autoFocus: false, enforceFocus: false, isOpen: isOpen, position: core_1.Position.BOTTOM_LEFT }, popoverProps, { className: (0, classnames_1.default)(listProps.className, popoverProps.className), content: React.createElement("div", { onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }, listProps.itemList), interactionKind: "click", onInteraction: _this.handlePopoverInteraction, popoverClassName: (0, classnames_1.default)(common_1.Classes.SELECT_POPOVER, popoverProps.popoverClassName), onOpening: _this.handlePopoverOpening, onOpened: _this.handlePopoverOpened }), | ||
React.createElement(core_1.InputGroup, tslib_1.__assign({ autoComplete: autoComplete, disabled: _this.props.disabled }, inputProps, { inputRef: _this.handleInputRef, onChange: listProps.handleQueryChange, onFocus: _this.handleInputFocus, onKeyDown: _this.getTargetKeyDownHandler(handleKeyDown), onKeyUp: _this.getTargetKeyUpHandler(handleKeyUp), placeholder: inputPlaceholder, value: inputValue })))); | ||
// N.B. no need to set `popoverProps.fill` since that is unused with the `renderTarget` API | ||
return (React.createElement(popover2_1.Popover2, tslib_1.__assign({ autoFocus: false, enforceFocus: false, isOpen: isOpen, position: core_1.Position.BOTTOM_LEFT }, popoverProps, { className: (0, classnames_1.default)(listProps.className, popoverProps.className), content: React.createElement("div", tslib_1.__assign({}, popoverContentProps, { onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }), listProps.itemList), interactionKind: "click", onInteraction: _this.handlePopoverInteraction, onOpened: _this.handlePopoverOpened, onOpening: _this.handlePopoverOpening, popoverClassName: (0, classnames_1.default)(common_1.Classes.SELECT_POPOVER, popoverProps.popoverClassName), popupKind: popover2_1.PopupKind.LISTBOX, ref: popoverRef, renderTarget: _this.getPopoverTargetRenderer(listProps, isOpen) }))); | ||
}; | ||
// We use the renderTarget API to flatten the rendered DOM and make it easier to implement features like | ||
// the "fill" prop. Note that we must take `isOpen` as an argument to force this render function to be called | ||
// again after that state changes. | ||
_this.getPopoverTargetRenderer = function (listProps, isOpen) { | ||
// eslint-disable-next-line react/display-name | ||
return function (_a) { | ||
var | ||
// pull out `isOpen` so that it's not forwarded to the DOM | ||
_isOpen = _a.isOpen, | ||
// pull out `defaultValue` due to type incompatibility with InputGroup. | ||
defaultValue = _a.defaultValue, ref = _a.ref, targetProps = tslib_1.__rest(_a, ["isOpen", "defaultValue", "ref"]); | ||
var _b = _this.props, disabled = _b.disabled, fill = _b.fill, _c = _b.inputProps, inputProps = _c === void 0 ? {} : _c, inputValueRenderer = _b.inputValueRenderer, resetOnClose = _b.resetOnClose; | ||
var selectedItem = _this.state.selectedItem; | ||
var handleKeyDown = listProps.handleKeyDown, handleKeyUp = listProps.handleKeyUp; | ||
var selectedItemText = selectedItem == null ? "" : inputValueRenderer(selectedItem); | ||
var _d = inputProps.autoComplete, autoComplete = _d === void 0 ? "off" : _d, _e = inputProps.placeholder, placeholder = _e === void 0 ? "Search..." : _e; | ||
// placeholder shows selected item while open. | ||
var inputPlaceholder = isOpen && selectedItemText ? selectedItemText : placeholder; | ||
// value shows query when open, and query remains when closed if nothing is selected. | ||
// if resetOnClose is enabled, then hide query when not open. (see handlePopoverOpening) | ||
var inputValue = isOpen ? listProps.query : selectedItemText !== null && selectedItemText !== void 0 ? selectedItemText : (resetOnClose ? "" : listProps.query); | ||
return (React.createElement(core_1.InputGroup, tslib_1.__assign({ autoComplete: autoComplete, disabled: disabled, "aria-controls": _this.listboxId }, targetProps, inputProps, { "aria-autocomplete": "list", "aria-expanded": isOpen, fill: fill, inputRef: (0, core_1.mergeRefs)(_this.handleInputRef, ref), onChange: listProps.handleQueryChange, onFocus: _this.handleInputFocus, onKeyDown: _this.getTargetKeyDownHandler(handleKeyDown), onKeyUp: _this.getTargetKeyUpHandler(handleKeyUp), placeholder: inputPlaceholder, role: "combobox", value: inputValue }))); | ||
}; | ||
}; | ||
_this.selectText = function () { | ||
@@ -174,3 +186,3 @@ // wait until the input is properly focused to select the text inside of it | ||
var _b = this.props, disabled = _b.disabled, inputProps = _b.inputProps, popoverProps = _b.popoverProps, restProps = tslib_1.__rest(_b, ["disabled", "inputProps", "popoverProps"]); | ||
return (React.createElement(this.TypedQueryList, tslib_1.__assign({}, restProps, { initialActiveItem: (_a = this.props.selectedItem) !== null && _a !== void 0 ? _a : undefined, onItemSelect: this.handleItemSelect, ref: this.handleQueryListRef, renderer: this.renderQueryList }))); | ||
return (React.createElement(this.TypedQueryList, tslib_1.__assign({}, restProps, { menuProps: { id: this.listboxId }, initialActiveItem: (_a = this.props.selectedItem) !== null && _a !== void 0 ? _a : undefined, onItemSelect: this.handleItemSelect, ref: this.handleQueryListRef, renderer: this.renderQueryList }))); | ||
}; | ||
@@ -177,0 +189,0 @@ Suggest2.prototype.componentDidUpdate = function (prevProps, prevState) { |
@@ -8,1 +8,2 @@ import * as Classes from "./classes"; | ||
export * from "./predicate"; | ||
export type { SelectPopoverProps } from "./selectPopoverProps"; |
import * as React from "react"; | ||
import { AbstractPureComponent2, TagInputProps } from "@blueprintjs/core"; | ||
import { Popover2, Popover2Props } from "@blueprintjs/popover2"; | ||
import { IListItemsProps } from "../../common"; | ||
import { IListItemsProps, SelectPopoverProps } from "../../common"; | ||
import { QueryList } from "../query-list/queryList"; | ||
export interface MultiSelect2Props<T> extends IListItemsProps<T> { | ||
export interface MultiSelect2Props<T> extends IListItemsProps<T>, SelectPopoverProps { | ||
/** | ||
* Whether the component is non-interactive. | ||
* If true, the list's item renderer will not be called. | ||
* | ||
* @default false | ||
*/ | ||
disabled?: boolean; | ||
/** | ||
* Whether the component should take up the full width of its container. | ||
@@ -13,2 +19,7 @@ * This overrides `popoverProps.fill` and `tagInputProps.fill`. | ||
/** | ||
* If provided, this component will render a "clear" button inside its TagInput. | ||
* Clicking that button will invoke this callback to clear all items from the current selection. | ||
*/ | ||
onClear?: () => void; | ||
/** | ||
* Callback invoked when an item is removed from the selection by | ||
@@ -42,15 +53,18 @@ * removing its tag in the TagInput. This is generally more useful than | ||
/** | ||
* Props to spread to `Popover2`. | ||
* | ||
* Note that `content` cannot be changed, but we do support attaching a ref to the Popover2 component | ||
* instance (sometimes useful to reposition the popover after updating `selectedItems` in reaction to | ||
* a change external to this component). | ||
* Props to add to the `div` that wraps the TagInput | ||
*/ | ||
popoverProps?: Partial<Omit<Popover2Props, "content"> & { | ||
ref: React.RefObject<Popover2<React.HTMLProps<HTMLDivElement>>>; | ||
}>; | ||
popoverTargetProps?: React.HTMLAttributes<HTMLDivElement>; | ||
/** Controlled selected values. */ | ||
selectedItems?: T[]; | ||
/** Props to spread to `TagInput`. Use `query` and `onQueryChange` to control the input. */ | ||
tagInputProps?: Partial<TagInputProps> & object; | ||
/** | ||
* Props to spread to `TagInput`. | ||
* If you wish to control the value of the input, use `query` and `onQueryChange` instead. | ||
* | ||
* Notes for `tagInputProps.rightElement`: | ||
* - you are responsible for disabling any elements you may render here when the overall | ||
* `MultiSelect2` is disabled. | ||
* - if the `onClear` prop is defined, this element will override/replace the default rightElement, | ||
* which is a "clear" button that removes all items from the current selection. | ||
*/ | ||
tagInputProps?: Partial<TagInputProps>; | ||
/** Custom renderer to transform an item into tag content. */ | ||
@@ -64,3 +78,5 @@ tagRenderer: (item: T) => React.ReactNode; | ||
static displayName: string; | ||
private listboxId; | ||
static defaultProps: { | ||
disabled: boolean; | ||
fill: boolean; | ||
@@ -78,2 +94,3 @@ placeholder: string; | ||
private renderQueryList; | ||
private getPopoverTargetRenderer; | ||
private handleItemSelect; | ||
@@ -84,4 +101,6 @@ private handleQueryChange; | ||
private handleTagRemove; | ||
private getTagInputAddHandler; | ||
private getTagInputKeyDownHandler; | ||
private getTagInputKeyUpHandler; | ||
private handleClearButtonClick; | ||
} |
@@ -18,4 +18,4 @@ /* | ||
import * as React from "react"; | ||
import { AbstractPureComponent2, Classes as CoreClasses, DISPLAYNAME_PREFIX, Keys, mergeRefs, refHandler, setRef, TagInput, } from "@blueprintjs/core"; | ||
import { Popover2 } from "@blueprintjs/popover2"; | ||
import { AbstractPureComponent2, Button, Classes as CoreClasses, DISPLAYNAME_PREFIX, Keys, mergeRefs, refHandler, setRef, TagInput, Utils, } from "@blueprintjs/core"; | ||
import { Popover2, PopupKind } from "@blueprintjs/popover2"; | ||
import { Classes } from "../../common"; | ||
@@ -29,2 +29,3 @@ import { QueryList } from "../query-list/queryList"; | ||
_this = _super.apply(this, arguments) || this; | ||
_this.listboxId = Utils.uniqueId("listbox"); | ||
_this.state = { | ||
@@ -42,23 +43,43 @@ isOpen: (_this.props.popoverProps && _this.props.popoverProps.isOpen) || false, | ||
_this.renderQueryList = function (listProps) { | ||
var _a; | ||
var _b = _this.props, fill = _b.fill, _c = _b.tagInputProps, tagInputProps = _c === void 0 ? {} : _c, _d = _b.popoverProps, popoverProps = _d === void 0 ? {} : _d, _e = _b.selectedItems, selectedItems = _e === void 0 ? [] : _e, placeholder = _b.placeholder; | ||
var handlePaste = listProps.handlePaste, handleKeyDown = listProps.handleKeyDown, handleKeyUp = listProps.handleKeyUp; | ||
if (fill) { | ||
popoverProps.fill = true; | ||
tagInputProps.fill = true; | ||
} | ||
// add our own inputProps.className so that we can reference it in event handlers | ||
var inputProps = __assign(__assign({}, tagInputProps.inputProps), { className: classNames((_a = tagInputProps.inputProps) === null || _a === void 0 ? void 0 : _a.className, Classes.MULTISELECT_TAG_INPUT_INPUT) }); | ||
var handleTagInputAdd = function (values, method) { | ||
if (method === "paste") { | ||
handlePaste(values); | ||
var _a = _this.props, disabled = _a.disabled, _b = _a.popoverContentProps, popoverContentProps = _b === void 0 ? {} : _b, _c = _a.popoverProps, popoverProps = _c === void 0 ? {} : _c; | ||
var handleKeyDown = listProps.handleKeyDown, handleKeyUp = listProps.handleKeyUp; | ||
var popoverRef = _this.props.popoverRef === undefined | ||
? _this.refHandlers.popover | ||
: mergeRefs(_this.refHandlers.popover, _this.props.popoverRef); | ||
// N.B. no need to set `popoverProps.fill` since that is unused with the `renderTarget` API | ||
return (React.createElement(Popover2, __assign({ autoFocus: false, canEscapeKeyClose: true, disabled: disabled, enforceFocus: false, isOpen: _this.state.isOpen, placement: popoverProps.position || popoverProps.placement ? undefined : "bottom-start" }, popoverProps, { className: classNames(listProps.className, popoverProps.className), content: React.createElement("div", __assign({}, popoverContentProps, { onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }), listProps.itemList), interactionKind: "click", onInteraction: _this.handlePopoverInteraction, onOpened: _this.handlePopoverOpened, popoverClassName: classNames(Classes.MULTISELECT_POPOVER, popoverProps.popoverClassName), popupKind: PopupKind.LISTBOX, ref: popoverRef, renderTarget: _this.getPopoverTargetRenderer(listProps, _this.state.isOpen) }))); | ||
}; | ||
// We use the renderTarget API to flatten the rendered DOM and make it easier to implement features like | ||
// the "fill" prop. Note that we must take `isOpen` as an argument to force this render function to be called | ||
// again after that state changes. | ||
_this.getPopoverTargetRenderer = function (listProps, isOpen) { | ||
// N.B. pull out `isOpen` so that it's not forwarded to the DOM, but remember not to use it directly | ||
// since it may be stale (`renderTarget` is not re-invoked on this.state changes). | ||
// eslint-disable-next-line react/display-name | ||
return function (_a) { | ||
var _b; | ||
var _c; | ||
var _isOpen = _a.isOpen, ref = _a.ref, targetProps = __rest(_a, ["isOpen", "ref"]); | ||
var _d = _this.props, disabled = _d.disabled, fill = _d.fill, onClear = _d.onClear, placeholder = _d.placeholder, _e = _d.popoverTargetProps, popoverTargetProps = _e === void 0 ? {} : _e, _f = _d.selectedItems, selectedItems = _f === void 0 ? [] : _f, _g = _d.tagInputProps, tagInputProps = _g === void 0 ? {} : _g; | ||
var handleKeyDown = listProps.handleKeyDown, handleKeyUp = listProps.handleKeyUp; | ||
if (disabled) { | ||
tagInputProps.disabled = true; | ||
} | ||
if (fill) { | ||
tagInputProps.fill = true; | ||
} | ||
// add our own inputProps.className so that we can reference it in event handlers | ||
var inputProps = __assign(__assign({}, tagInputProps.inputProps), { className: classNames((_c = tagInputProps.inputProps) === null || _c === void 0 ? void 0 : _c.className, Classes.MULTISELECT_TAG_INPUT_INPUT) }); | ||
var maybeClearButton = onClear !== undefined && selectedItems.length > 0 ? (React.createElement(Button, { disabled: disabled, icon: "cross", minimal: true, onClick: _this.handleClearButtonClick })) : undefined; | ||
return (React.createElement("div", __assign({ "aria-autocomplete": "list", "aria-controls": _this.listboxId }, popoverTargetProps, targetProps, { "aria-expanded": isOpen, | ||
// Note that we must set FILL here in addition to TagInput to get the wrapper element to full width | ||
className: classNames(targetProps.className, popoverTargetProps.className, (_b = {}, | ||
_b[CoreClasses.FILL] = fill, | ||
_b)), | ||
// Normally, Popover2 would also need to attach its own `onKeyDown` handler via `targetProps`, | ||
// but in our case we fully manage that interaction and listen for key events to open/close | ||
// the popover, so we elide it from the DOM. | ||
onKeyDown: _this.getTagInputKeyDownHandler(handleKeyDown), onKeyUp: _this.getTagInputKeyUpHandler(handleKeyUp), ref: ref, role: "combobox" }), | ||
React.createElement(TagInput, __assign({ placeholder: placeholder, rightElement: maybeClearButton }, tagInputProps, { className: classNames(Classes.MULTISELECT, tagInputProps.className), inputRef: _this.refHandlers.input, inputProps: inputProps, inputValue: listProps.query, onAdd: _this.getTagInputAddHandler(listProps), onInputChange: listProps.handleQueryChange, onRemove: _this.handleTagRemove, values: selectedItems.map(_this.props.tagRenderer) })))); | ||
}; | ||
return (React.createElement(Popover2, __assign({ autoFocus: false, canEscapeKeyClose: true, enforceFocus: false, isOpen: _this.state.isOpen, placement: "bottom-start" }, popoverProps, { className: classNames(listProps.className, popoverProps.className), content: React.createElement("div", { onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }, listProps.itemList), interactionKind: "click", onInteraction: _this.handlePopoverInteraction, onOpened: _this.handlePopoverOpened, popoverClassName: classNames(Classes.MULTISELECT_POPOVER, popoverProps.popoverClassName), ref: popoverProps.ref === undefined | ||
? _this.refHandlers.popover | ||
: mergeRefs(_this.refHandlers.popover, popoverProps.ref) }), | ||
React.createElement("div", { onKeyDown: _this.getTagInputKeyDownHandler(handleKeyDown), onKeyUp: _this.getTagInputKeyUpHandler(handleKeyUp) }, | ||
React.createElement(TagInput, __assign({ placeholder: placeholder }, tagInputProps, { className: classNames(Classes.MULTISELECT, tagInputProps.className), inputRef: _this.refHandlers.input, inputProps: inputProps, inputValue: listProps.query, | ||
/* eslint-disable-next-line react/jsx-no-bind */ | ||
onAdd: handleTagInputAdd, onInputChange: listProps.handleQueryChange, onRemove: _this.handleTagRemove, values: selectedItems.map(_this.props.tagRenderer) }))))); | ||
}; | ||
@@ -110,2 +131,7 @@ _this.handleItemSelect = function (item, evt) { | ||
}; | ||
_this.getTagInputAddHandler = function (listProps) { return function (values, method) { | ||
if (method === "paste") { | ||
listProps.handlePaste(values); | ||
} | ||
}; }; | ||
_this.getTagInputKeyDownHandler = function (handleQueryListKeyDown) { | ||
@@ -143,2 +169,7 @@ return function (e) { | ||
}; | ||
_this.handleClearButtonClick = function () { | ||
var _a, _b, _c; | ||
(_b = (_a = _this.props).onClear) === null || _b === void 0 ? void 0 : _b.call(_a); | ||
(_c = _this.refHandlers.popover.current) === null || _c === void 0 ? void 0 : _c.reposition(); // reposition when size of input changes | ||
}; | ||
return _this; | ||
@@ -156,2 +187,6 @@ } | ||
} | ||
if ((prevProps.onClear === undefined && this.props.onClear !== undefined) || | ||
(prevProps.onClear !== undefined && this.props.onClear === undefined)) { | ||
this.forceUpdate(); | ||
} | ||
}; | ||
@@ -161,6 +196,7 @@ MultiSelect2.prototype.render = function () { | ||
var _a = this.props, openOnKeyDown = _a.openOnKeyDown, popoverProps = _a.popoverProps, tagInputProps = _a.tagInputProps, restProps = __rest(_a, ["openOnKeyDown", "popoverProps", "tagInputProps"]); | ||
return (React.createElement(this.TypedQueryList, __assign({}, restProps, { onItemSelect: this.handleItemSelect, onQueryChange: this.handleQueryChange, ref: this.refHandlers.queryList, renderer: this.renderQueryList }))); | ||
return (React.createElement(this.TypedQueryList, __assign({}, restProps, { menuProps: { "aria-multiselectable": true, id: this.listboxId }, onItemSelect: this.handleItemSelect, onQueryChange: this.handleQueryChange, ref: this.refHandlers.queryList, renderer: this.renderQueryList }))); | ||
}; | ||
MultiSelect2.displayName = "".concat(DISPLAYNAME_PREFIX, ".MultiSelect2"); | ||
MultiSelect2.defaultProps = { | ||
disabled: false, | ||
fill: false, | ||
@@ -167,0 +203,0 @@ placeholder: "Search...", |
@@ -13,2 +13,6 @@ import * as React from "react"; | ||
/** | ||
* 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 | ||
@@ -15,0 +19,0 @@ * (up/down to navigate active item). This callback is passed to `renderer` and (along with |
@@ -57,3 +57,3 @@ /* | ||
_this.renderItemList = function (listProps) { | ||
var _a = _this.props, initialContent = _a.initialContent, noResults = _a.noResults; | ||
var _a = _this.props, initialContent = _a.initialContent, noResults = _a.noResults, menuProps = _a.menuProps; | ||
// omit noResults if createNewItemFromQuery and createNewItemRenderer are both supplied, and query is not empty | ||
@@ -67,3 +67,3 @@ var createItemView = listProps.renderCreateItem(); | ||
var createFirst = _this.isCreateItemFirst(); | ||
return (React.createElement(Menu, { ulRef: listProps.itemsParentRef }, | ||
return (React.createElement(Menu, __assign({ role: "listbox" }, menuProps, { ulRef: listProps.itemsParentRef }), | ||
createFirst && createItemView, | ||
@@ -70,0 +70,0 @@ menuContent, |
import * as React from "react"; | ||
import { AbstractPureComponent2, InputGroupProps2 } from "@blueprintjs/core"; | ||
import { Popover2Props } from "@blueprintjs/popover2"; | ||
import { IListItemsProps } from "../../common"; | ||
export interface Select2Props<T> extends IListItemsProps<T> { | ||
import { IListItemsProps, SelectPopoverProps } from "../../common"; | ||
export interface Select2Props<T> extends IListItemsProps<T>, SelectPopoverProps { | ||
/** | ||
* Element which triggers the select popover. In most cases, you should display | ||
* the name or label of the curently selected item here. | ||
*/ | ||
children?: React.ReactNode; | ||
/** | ||
* Whether the component is non-interactive. | ||
* If true, the list's item renderer will not be called. | ||
* Note that you'll also need to disable the component's children, if appropriate. | ||
* | ||
* @default false | ||
*/ | ||
disabled?: boolean; | ||
/** | ||
* Whether the component should take up the full width of its container. | ||
@@ -21,10 +32,2 @@ * This overrides `popoverProps.fill`. You also have to ensure that the child | ||
/** | ||
* Whether the component is non-interactive. | ||
* If true, the list's item renderer will not be called. | ||
* Note that you'll also need to disable the component's children, if appropriate. | ||
* | ||
* @default false | ||
*/ | ||
disabled?: boolean; | ||
/** | ||
* Props to spread to the query `InputGroup`. Use `query` and | ||
@@ -35,5 +38,7 @@ * `onQueryChange` instead of `inputProps.value` and `inputProps.onChange` | ||
inputProps?: InputGroupProps2; | ||
/** Props to spread to `Popover2`. Note that `content` cannot be changed. */ | ||
popoverProps?: Partial<Omit<Popover2Props, "content">>; | ||
/** | ||
* Props to add to the popover target wrapper element. | ||
*/ | ||
popoverTargetProps?: React.HTMLAttributes<HTMLDivElement>; | ||
/** | ||
* Whether the active item should be reset to the first matching item _when | ||
@@ -53,4 +58,4 @@ * the popover closes_. The query will also be reset to the empty string. | ||
state: Select2State; | ||
inputElement: HTMLInputElement | null; | ||
private TypedQueryList; | ||
inputElement: HTMLInputElement | null; | ||
private queryList; | ||
@@ -60,6 +65,11 @@ private previousFocusedElement; | ||
private handleQueryListRef; | ||
private listboxId; | ||
render(): JSX.Element; | ||
componentDidUpdate(prevProps: Select2Props<T>, prevState: Select2State): void; | ||
private renderQueryList; | ||
private getPopoverTargetRenderer; | ||
private maybeRenderClearButton; | ||
/** | ||
* Target wrapper element "keydown" handler while the popover is closed. | ||
*/ | ||
private handleTargetKeyDown; | ||
@@ -66,0 +76,0 @@ private handleItemSelect; |
@@ -20,4 +20,4 @@ /* | ||
import * as React from "react"; | ||
import { AbstractPureComponent2, Button, DISPLAYNAME_PREFIX, InputGroup, Keys, Position, refHandler, setRef, } from "@blueprintjs/core"; | ||
import { Popover2 } from "@blueprintjs/popover2"; | ||
import { AbstractPureComponent2, Button, Classes as CoreClasses, DISPLAYNAME_PREFIX, InputGroup, Keys, Position, refHandler, setRef, Utils, } from "@blueprintjs/core"; | ||
import { Popover2, PopupKind } from "@blueprintjs/popover2"; | ||
import { Classes } from "../../common"; | ||
@@ -32,24 +32,50 @@ import { QueryList } from "../query-list/queryList"; | ||
_this.state = { isOpen: false }; | ||
_this.inputElement = null; | ||
_this.TypedQueryList = QueryList.ofType(); | ||
_this.inputElement = null; | ||
_this.queryList = null; | ||
_this.handleInputRef = refHandler(_this, "inputElement", (_a = _this.props.inputProps) === null || _a === void 0 ? void 0 : _a.inputRef); | ||
_this.handleQueryListRef = function (ref) { return (_this.queryList = ref); }; | ||
_this.listboxId = Utils.uniqueId("listbox"); | ||
_this.renderQueryList = function (listProps) { | ||
// not using defaultProps cuz they're hard to type with generics (can't use <T> on static members) | ||
var _a = _this.props, fill = _a.fill, _b = _a.filterable, filterable = _b === void 0 ? true : _b, _c = _a.disabled, disabled = _c === void 0 ? false : _c, _d = _a.inputProps, inputProps = _d === void 0 ? {} : _d, _e = _a.popoverProps, popoverProps = _e === void 0 ? {} : _e; | ||
var _a = _this.props, fill = _a.fill, _b = _a.filterable, filterable = _b === void 0 ? true : _b, _c = _a.disabled, disabled = _c === void 0 ? false : _c, _d = _a.inputProps, inputProps = _d === void 0 ? {} : _d, _e = _a.popoverContentProps, popoverContentProps = _e === void 0 ? {} : _e, _f = _a.popoverProps, popoverProps = _f === void 0 ? {} : _f, popoverRef = _a.popoverRef; | ||
if (fill) { | ||
popoverProps.fill = true; | ||
} | ||
var input = (React.createElement(InputGroup, __assign({ leftIcon: "search", placeholder: "Filter...", rightElement: _this.maybeRenderClearButton(listProps.query) }, inputProps, { inputRef: _this.handleInputRef, onChange: listProps.handleQueryChange, value: listProps.query }))); | ||
var input = (React.createElement(InputGroup, __assign({ "aria-autocomplete": "list", leftIcon: "search", placeholder: "Filter...", rightElement: _this.maybeRenderClearButton(listProps.query) }, inputProps, { inputRef: _this.handleInputRef, onChange: listProps.handleQueryChange, value: listProps.query }))); | ||
var handleKeyDown = listProps.handleKeyDown, handleKeyUp = listProps.handleKeyUp; | ||
return (React.createElement(Popover2, __assign({ autoFocus: false, enforceFocus: false, isOpen: _this.state.isOpen, disabled: disabled, position: Position.BOTTOM_LEFT }, popoverProps, { className: classNames(listProps.className, popoverProps.className), content: React.createElement("div", { onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }, | ||
return (React.createElement(Popover2, __assign({ autoFocus: false, enforceFocus: false, isOpen: _this.state.isOpen, disabled: disabled, position: Position.BOTTOM_LEFT }, popoverProps, { className: classNames(listProps.className, popoverProps.className), content: React.createElement("div", __assign({}, popoverContentProps, { onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }), | ||
filterable ? input : undefined, | ||
listProps.itemList), onInteraction: _this.handlePopoverInteraction, popoverClassName: classNames(Classes.SELECT_POPOVER, popoverProps.popoverClassName), onOpening: _this.handlePopoverOpening, onOpened: _this.handlePopoverOpened, onClosing: _this.handlePopoverClosing }), | ||
React.createElement("div", { onKeyDown: _this.state.isOpen ? handleKeyDown : _this.handleTargetKeyDown, onKeyUp: _this.state.isOpen ? handleKeyUp : undefined }, _this.props.children))); | ||
listProps.itemList), onClosing: _this.handlePopoverClosing, onInteraction: _this.handlePopoverInteraction, onOpened: _this.handlePopoverOpened, onOpening: _this.handlePopoverOpening, popoverClassName: classNames(Classes.SELECT_POPOVER, popoverProps.popoverClassName), popupKind: PopupKind.LISTBOX, ref: popoverRef, renderTarget: _this.getPopoverTargetRenderer(listProps, _this.state.isOpen) }))); | ||
}; | ||
// We use the renderTarget API to flatten the rendered DOM and make it easier to implement features like | ||
// the "fill" prop. Note that we must take `isOpen` as an argument to force this render function to be called | ||
// again after that state changes. | ||
_this.getPopoverTargetRenderer = function (listProps, isOpen) { | ||
// N.B. pull out `isOpen` so that it's not forwarded to the DOM, but remember not to use it directly | ||
// since it may be stale (`renderTarget` is not re-invoked on this.state changes). | ||
// eslint-disable-next-line react/display-name | ||
return function (_a) { | ||
var _b; | ||
var _isOpen = _a.isOpen, ref = _a.ref, targetProps = __rest(_a, ["isOpen", "ref"]); | ||
var popoverTargetProps = _this.props.popoverTargetProps; | ||
var handleKeyDown = listProps.handleKeyDown, handleKeyUp = listProps.handleKeyUp; | ||
return (React.createElement("div", __assign({ "aria-controls": _this.listboxId }, popoverTargetProps, targetProps, { "aria-expanded": isOpen, | ||
// Note that we must set FILL here in addition to children to get the wrapper element to full width | ||
className: classNames(targetProps.className, popoverTargetProps === null || popoverTargetProps === void 0 ? void 0 : popoverTargetProps.className, (_b = {}, | ||
_b[CoreClasses.FILL] = _this.props.fill, | ||
_b)), | ||
// Normally, Popover2 would also need to attach its own `onKeyDown` handler via `targetProps`, | ||
// but in our case we fully manage that interaction and listen for key events to open/close | ||
// the popover, so we elide it from the DOM. | ||
onKeyDown: isOpen ? handleKeyDown : _this.handleTargetKeyDown, onKeyUp: isOpen ? handleKeyUp : undefined, ref: ref, role: "combobox" }), _this.props.children)); | ||
}; | ||
}; | ||
/** | ||
* Target wrapper element "keydown" handler while the popover is closed. | ||
*/ | ||
_this.handleTargetKeyDown = function (event) { | ||
// open popover when arrow key pressed on target while closed | ||
// HACKHACK: https://github.com/palantir/blueprint/issues/4165 | ||
// eslint-disable-next-line deprecation/deprecation | ||
/* eslint-disable deprecation/deprecation */ | ||
if (event.which === Keys.ARROW_UP || event.which === Keys.ARROW_DOWN) { | ||
@@ -59,2 +85,6 @@ event.preventDefault(); | ||
} | ||
else if (Keys.isKeyboardClick(event.keyCode)) { | ||
_this.setState({ isOpen: true }); | ||
} | ||
/* eslint-enable deprecation/deprecation */ | ||
}; | ||
@@ -118,3 +148,3 @@ _this.handleItemSelect = function (item, event) { | ||
var _a = this.props, filterable = _a.filterable, inputProps = _a.inputProps, popoverProps = _a.popoverProps, restProps = __rest(_a, ["filterable", "inputProps", "popoverProps"]); | ||
return (React.createElement(this.TypedQueryList, __assign({}, restProps, { onItemSelect: this.handleItemSelect, ref: this.handleQueryListRef, renderer: this.renderQueryList }))); | ||
return (React.createElement(this.TypedQueryList, __assign({}, restProps, { menuProps: { id: this.listboxId }, onItemSelect: this.handleItemSelect, ref: this.handleQueryListRef, renderer: this.renderQueryList }))); | ||
}; | ||
@@ -121,0 +151,0 @@ Select2.prototype.componentDidUpdate = function (prevProps, prevState) { |
/// <reference types="react" /> | ||
import { AbstractPureComponent2, InputGroupProps2 } from "@blueprintjs/core"; | ||
import { Popover2Props } from "@blueprintjs/popover2"; | ||
import { IListItemsProps } from "../../common"; | ||
export interface Suggest2Props<T> extends IListItemsProps<T> { | ||
import { IListItemsProps, SelectPopoverProps } from "../../common"; | ||
export interface Suggest2Props<T> extends IListItemsProps<T>, SelectPopoverProps { | ||
/** | ||
@@ -48,4 +47,2 @@ * Whether the popover should close after selecting an item. | ||
openOnKeyDown?: boolean; | ||
/** Props to spread to `Popover2`. Note that `content` cannot be changed. */ | ||
popoverProps?: Partial<Omit<Popover2Props, "content">>; | ||
/** | ||
@@ -73,5 +70,7 @@ * Whether the active item should be reset to the first matching item _when | ||
private handleQueryListRef; | ||
private listboxId; | ||
render(): JSX.Element; | ||
componentDidUpdate(prevProps: Suggest2Props<T>, prevState: Suggest2State<T>): void; | ||
private renderQueryList; | ||
private getPopoverTargetRenderer; | ||
private selectText; | ||
@@ -78,0 +77,0 @@ private handleInputFocus; |
@@ -20,4 +20,4 @@ /* | ||
import * as React from "react"; | ||
import { AbstractPureComponent2, DISPLAYNAME_PREFIX, InputGroup, Keys, Position, refHandler, setRef, } from "@blueprintjs/core"; | ||
import { Popover2 } from "@blueprintjs/popover2"; | ||
import { AbstractPureComponent2, DISPLAYNAME_PREFIX, InputGroup, Keys, mergeRefs, Position, refHandler, setRef, Utils, } from "@blueprintjs/core"; | ||
import { Popover2, PopupKind } from "@blueprintjs/popover2"; | ||
import { Classes } from "../../common"; | ||
@@ -40,22 +40,34 @@ import { QueryList } from "../query-list/queryList"; | ||
_this.handleQueryListRef = function (ref) { return (_this.queryList = ref); }; | ||
_this.listboxId = Utils.uniqueId("listbox"); | ||
_this.renderQueryList = function (listProps) { | ||
var _a = _this.props, fill = _a.fill, _b = _a.inputProps, inputProps = _b === void 0 ? {} : _b, _c = _a.popoverProps, popoverProps = _c === void 0 ? {} : _c; | ||
var _d = _this.state, isOpen = _d.isOpen, selectedItem = _d.selectedItem; | ||
var _a = _this.props, _b = _a.popoverContentProps, popoverContentProps = _b === void 0 ? {} : _b, _c = _a.popoverProps, popoverProps = _c === void 0 ? {} : _c, popoverRef = _a.popoverRef; | ||
var isOpen = _this.state.isOpen; | ||
var handleKeyDown = listProps.handleKeyDown, handleKeyUp = listProps.handleKeyUp; | ||
var _e = inputProps.autoComplete, autoComplete = _e === void 0 ? "off" : _e, _f = inputProps.placeholder, placeholder = _f === void 0 ? "Search..." : _f; | ||
var selectedItemText = selectedItem ? _this.props.inputValueRenderer(selectedItem) : ""; | ||
// placeholder shows selected item while open. | ||
var inputPlaceholder = isOpen && selectedItemText ? selectedItemText : placeholder; | ||
// value shows query when open, and query remains when closed if nothing is selected. | ||
// if resetOnClose is enabled, then hide query when not open. (see handlePopoverOpening) | ||
var inputValue = isOpen | ||
? listProps.query | ||
: selectedItemText || (_this.props.resetOnClose ? "" : listProps.query); | ||
if (fill) { | ||
popoverProps.fill = true; | ||
inputProps.fill = true; | ||
} | ||
return (React.createElement(Popover2, __assign({ autoFocus: false, enforceFocus: false, isOpen: isOpen, position: Position.BOTTOM_LEFT }, popoverProps, { className: classNames(listProps.className, popoverProps.className), content: React.createElement("div", { onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }, listProps.itemList), interactionKind: "click", onInteraction: _this.handlePopoverInteraction, popoverClassName: classNames(Classes.SELECT_POPOVER, popoverProps.popoverClassName), onOpening: _this.handlePopoverOpening, onOpened: _this.handlePopoverOpened }), | ||
React.createElement(InputGroup, __assign({ autoComplete: autoComplete, disabled: _this.props.disabled }, inputProps, { inputRef: _this.handleInputRef, onChange: listProps.handleQueryChange, onFocus: _this.handleInputFocus, onKeyDown: _this.getTargetKeyDownHandler(handleKeyDown), onKeyUp: _this.getTargetKeyUpHandler(handleKeyUp), placeholder: inputPlaceholder, value: inputValue })))); | ||
// N.B. no need to set `popoverProps.fill` since that is unused with the `renderTarget` API | ||
return (React.createElement(Popover2, __assign({ autoFocus: false, enforceFocus: false, isOpen: isOpen, position: Position.BOTTOM_LEFT }, popoverProps, { className: classNames(listProps.className, popoverProps.className), content: React.createElement("div", __assign({}, popoverContentProps, { onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }), listProps.itemList), interactionKind: "click", onInteraction: _this.handlePopoverInteraction, onOpened: _this.handlePopoverOpened, onOpening: _this.handlePopoverOpening, popoverClassName: classNames(Classes.SELECT_POPOVER, popoverProps.popoverClassName), popupKind: PopupKind.LISTBOX, ref: popoverRef, renderTarget: _this.getPopoverTargetRenderer(listProps, isOpen) }))); | ||
}; | ||
// We use the renderTarget API to flatten the rendered DOM and make it easier to implement features like | ||
// the "fill" prop. Note that we must take `isOpen` as an argument to force this render function to be called | ||
// again after that state changes. | ||
_this.getPopoverTargetRenderer = function (listProps, isOpen) { | ||
// eslint-disable-next-line react/display-name | ||
return function (_a) { | ||
var | ||
// pull out `isOpen` so that it's not forwarded to the DOM | ||
_isOpen = _a.isOpen, | ||
// pull out `defaultValue` due to type incompatibility with InputGroup. | ||
defaultValue = _a.defaultValue, ref = _a.ref, targetProps = __rest(_a, ["isOpen", "defaultValue", "ref"]); | ||
var _b = _this.props, disabled = _b.disabled, fill = _b.fill, _c = _b.inputProps, inputProps = _c === void 0 ? {} : _c, inputValueRenderer = _b.inputValueRenderer, resetOnClose = _b.resetOnClose; | ||
var selectedItem = _this.state.selectedItem; | ||
var handleKeyDown = listProps.handleKeyDown, handleKeyUp = listProps.handleKeyUp; | ||
var selectedItemText = selectedItem == null ? "" : inputValueRenderer(selectedItem); | ||
var _d = inputProps.autoComplete, autoComplete = _d === void 0 ? "off" : _d, _e = inputProps.placeholder, placeholder = _e === void 0 ? "Search..." : _e; | ||
// placeholder shows selected item while open. | ||
var inputPlaceholder = isOpen && selectedItemText ? selectedItemText : placeholder; | ||
// value shows query when open, and query remains when closed if nothing is selected. | ||
// if resetOnClose is enabled, then hide query when not open. (see handlePopoverOpening) | ||
var inputValue = isOpen ? listProps.query : selectedItemText !== null && selectedItemText !== void 0 ? selectedItemText : (resetOnClose ? "" : listProps.query); | ||
return (React.createElement(InputGroup, __assign({ autoComplete: autoComplete, disabled: disabled, "aria-controls": _this.listboxId }, targetProps, inputProps, { "aria-autocomplete": "list", "aria-expanded": isOpen, fill: fill, inputRef: mergeRefs(_this.handleInputRef, ref), onChange: listProps.handleQueryChange, onFocus: _this.handleInputFocus, onKeyDown: _this.getTargetKeyDownHandler(handleKeyDown), onKeyUp: _this.getTargetKeyUpHandler(handleKeyUp), placeholder: inputPlaceholder, role: "combobox", value: inputValue }))); | ||
}; | ||
}; | ||
_this.selectText = function () { | ||
@@ -172,3 +184,3 @@ // wait until the input is properly focused to select the text inside of it | ||
var _b = this.props, disabled = _b.disabled, inputProps = _b.inputProps, popoverProps = _b.popoverProps, restProps = __rest(_b, ["disabled", "inputProps", "popoverProps"]); | ||
return (React.createElement(this.TypedQueryList, __assign({}, restProps, { initialActiveItem: (_a = this.props.selectedItem) !== null && _a !== void 0 ? _a : undefined, onItemSelect: this.handleItemSelect, ref: this.handleQueryListRef, renderer: this.renderQueryList }))); | ||
return (React.createElement(this.TypedQueryList, __assign({}, restProps, { menuProps: { id: this.listboxId }, initialActiveItem: (_a = this.props.selectedItem) !== null && _a !== void 0 ? _a : undefined, onItemSelect: this.handleItemSelect, ref: this.handleQueryListRef, renderer: this.renderQueryList }))); | ||
}; | ||
@@ -175,0 +187,0 @@ Suggest2.prototype.componentDidUpdate = function (prevProps, prevState) { |
@@ -8,1 +8,2 @@ import * as Classes from "./classes"; | ||
export * from "./predicate"; | ||
export type { SelectPopoverProps } from "./selectPopoverProps"; |
import * as React from "react"; | ||
import { AbstractPureComponent2, TagInputProps } from "@blueprintjs/core"; | ||
import { Popover2, Popover2Props } from "@blueprintjs/popover2"; | ||
import { IListItemsProps } from "../../common"; | ||
import { IListItemsProps, SelectPopoverProps } from "../../common"; | ||
import { QueryList } from "../query-list/queryList"; | ||
export interface MultiSelect2Props<T> extends IListItemsProps<T> { | ||
export interface MultiSelect2Props<T> extends IListItemsProps<T>, SelectPopoverProps { | ||
/** | ||
* Whether the component is non-interactive. | ||
* If true, the list's item renderer will not be called. | ||
* | ||
* @default false | ||
*/ | ||
disabled?: boolean; | ||
/** | ||
* Whether the component should take up the full width of its container. | ||
@@ -13,2 +19,7 @@ * This overrides `popoverProps.fill` and `tagInputProps.fill`. | ||
/** | ||
* If provided, this component will render a "clear" button inside its TagInput. | ||
* Clicking that button will invoke this callback to clear all items from the current selection. | ||
*/ | ||
onClear?: () => void; | ||
/** | ||
* Callback invoked when an item is removed from the selection by | ||
@@ -42,15 +53,18 @@ * removing its tag in the TagInput. This is generally more useful than | ||
/** | ||
* Props to spread to `Popover2`. | ||
* | ||
* Note that `content` cannot be changed, but we do support attaching a ref to the Popover2 component | ||
* instance (sometimes useful to reposition the popover after updating `selectedItems` in reaction to | ||
* a change external to this component). | ||
* Props to add to the `div` that wraps the TagInput | ||
*/ | ||
popoverProps?: Partial<Omit<Popover2Props, "content"> & { | ||
ref: React.RefObject<Popover2<React.HTMLProps<HTMLDivElement>>>; | ||
}>; | ||
popoverTargetProps?: React.HTMLAttributes<HTMLDivElement>; | ||
/** Controlled selected values. */ | ||
selectedItems?: T[]; | ||
/** Props to spread to `TagInput`. Use `query` and `onQueryChange` to control the input. */ | ||
tagInputProps?: Partial<TagInputProps> & object; | ||
/** | ||
* Props to spread to `TagInput`. | ||
* If you wish to control the value of the input, use `query` and `onQueryChange` instead. | ||
* | ||
* Notes for `tagInputProps.rightElement`: | ||
* - you are responsible for disabling any elements you may render here when the overall | ||
* `MultiSelect2` is disabled. | ||
* - if the `onClear` prop is defined, this element will override/replace the default rightElement, | ||
* which is a "clear" button that removes all items from the current selection. | ||
*/ | ||
tagInputProps?: Partial<TagInputProps>; | ||
/** Custom renderer to transform an item into tag content. */ | ||
@@ -64,3 +78,5 @@ tagRenderer: (item: T) => React.ReactNode; | ||
static displayName: string; | ||
private listboxId; | ||
static defaultProps: { | ||
disabled: boolean; | ||
fill: boolean; | ||
@@ -78,2 +94,3 @@ placeholder: string; | ||
private renderQueryList; | ||
private getPopoverTargetRenderer; | ||
private handleItemSelect; | ||
@@ -84,4 +101,6 @@ private handleQueryChange; | ||
private handleTagRemove; | ||
private getTagInputAddHandler; | ||
private getTagInputKeyDownHandler; | ||
private getTagInputKeyUpHandler; | ||
private handleClearButtonClick; | ||
} |
@@ -17,4 +17,4 @@ /* | ||
import * as React from "react"; | ||
import { AbstractPureComponent2, Classes as CoreClasses, DISPLAYNAME_PREFIX, Keys, mergeRefs, refHandler, setRef, TagInput, } from "@blueprintjs/core"; | ||
import { Popover2 } from "@blueprintjs/popover2"; | ||
import { AbstractPureComponent2, Button, Classes as CoreClasses, DISPLAYNAME_PREFIX, Keys, mergeRefs, refHandler, setRef, TagInput, Utils, } from "@blueprintjs/core"; | ||
import { Popover2, PopupKind } from "@blueprintjs/popover2"; | ||
import { Classes } from "../../common"; | ||
@@ -24,3 +24,5 @@ import { QueryList } from "../query-list/queryList"; | ||
static displayName = `${DISPLAYNAME_PREFIX}.MultiSelect2`; | ||
listboxId = Utils.uniqueId("listbox"); | ||
static defaultProps = { | ||
disabled: false, | ||
fill: false, | ||
@@ -49,2 +51,6 @@ placeholder: "Search...", | ||
} | ||
if ((prevProps.onClear === undefined && this.props.onClear !== undefined) || | ||
(prevProps.onClear !== undefined && this.props.onClear === undefined)) { | ||
this.forceUpdate(); | ||
} | ||
} | ||
@@ -54,9 +60,27 @@ render() { | ||
const { openOnKeyDown, popoverProps, tagInputProps, ...restProps } = this.props; | ||
return (React.createElement(this.TypedQueryList, { ...restProps, onItemSelect: this.handleItemSelect, onQueryChange: this.handleQueryChange, ref: this.refHandlers.queryList, renderer: this.renderQueryList })); | ||
return (React.createElement(this.TypedQueryList, { ...restProps, menuProps: { "aria-multiselectable": true, id: this.listboxId }, onItemSelect: this.handleItemSelect, onQueryChange: this.handleQueryChange, ref: this.refHandlers.queryList, renderer: this.renderQueryList })); | ||
} | ||
renderQueryList = (listProps) => { | ||
const { fill, tagInputProps = {}, popoverProps = {}, selectedItems = [], placeholder } = this.props; | ||
const { handlePaste, handleKeyDown, handleKeyUp } = listProps; | ||
const { disabled, popoverContentProps = {}, popoverProps = {} } = this.props; | ||
const { handleKeyDown, handleKeyUp } = listProps; | ||
const popoverRef = this.props.popoverRef === undefined | ||
? this.refHandlers.popover | ||
: mergeRefs(this.refHandlers.popover, this.props.popoverRef); | ||
// N.B. no need to set `popoverProps.fill` since that is unused with the `renderTarget` API | ||
return (React.createElement(Popover2, { autoFocus: false, canEscapeKeyClose: true, disabled: disabled, enforceFocus: false, isOpen: this.state.isOpen, placement: popoverProps.position || popoverProps.placement ? undefined : "bottom-start", ...popoverProps, className: classNames(listProps.className, popoverProps.className), content: React.createElement("div", { ...popoverContentProps, onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }, listProps.itemList), interactionKind: "click", onInteraction: this.handlePopoverInteraction, onOpened: this.handlePopoverOpened, popoverClassName: classNames(Classes.MULTISELECT_POPOVER, popoverProps.popoverClassName), popupKind: PopupKind.LISTBOX, ref: popoverRef, renderTarget: this.getPopoverTargetRenderer(listProps, this.state.isOpen) })); | ||
}; | ||
// We use the renderTarget API to flatten the rendered DOM and make it easier to implement features like | ||
// the "fill" prop. Note that we must take `isOpen` as an argument to force this render function to be called | ||
// again after that state changes. | ||
getPopoverTargetRenderer = (listProps, isOpen) => | ||
// N.B. pull out `isOpen` so that it's not forwarded to the DOM, but remember not to use it directly | ||
// since it may be stale (`renderTarget` is not re-invoked on this.state changes). | ||
// eslint-disable-next-line react/display-name | ||
({ isOpen: _isOpen, ref, ...targetProps }) => { | ||
const { disabled, fill, onClear, placeholder, popoverTargetProps = {}, selectedItems = [], tagInputProps = {}, } = this.props; | ||
const { handleKeyDown, handleKeyUp } = listProps; | ||
if (disabled) { | ||
tagInputProps.disabled = true; | ||
} | ||
if (fill) { | ||
popoverProps.fill = true; | ||
tagInputProps.fill = true; | ||
@@ -69,14 +93,13 @@ } | ||
}; | ||
const handleTagInputAdd = (values, method) => { | ||
if (method === "paste") { | ||
handlePaste(values); | ||
} | ||
}; | ||
return (React.createElement(Popover2, { autoFocus: false, canEscapeKeyClose: true, enforceFocus: false, isOpen: this.state.isOpen, placement: "bottom-start", ...popoverProps, className: classNames(listProps.className, popoverProps.className), content: React.createElement("div", { onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }, listProps.itemList), interactionKind: "click", onInteraction: this.handlePopoverInteraction, onOpened: this.handlePopoverOpened, popoverClassName: classNames(Classes.MULTISELECT_POPOVER, popoverProps.popoverClassName), ref: popoverProps.ref === undefined | ||
? this.refHandlers.popover | ||
: mergeRefs(this.refHandlers.popover, popoverProps.ref) }, | ||
React.createElement("div", { onKeyDown: this.getTagInputKeyDownHandler(handleKeyDown), onKeyUp: this.getTagInputKeyUpHandler(handleKeyUp) }, | ||
React.createElement(TagInput, { placeholder: placeholder, ...tagInputProps, className: classNames(Classes.MULTISELECT, tagInputProps.className), inputRef: this.refHandlers.input, inputProps: inputProps, inputValue: listProps.query, | ||
/* eslint-disable-next-line react/jsx-no-bind */ | ||
onAdd: handleTagInputAdd, onInputChange: listProps.handleQueryChange, onRemove: this.handleTagRemove, values: selectedItems.map(this.props.tagRenderer) })))); | ||
const maybeClearButton = onClear !== undefined && selectedItems.length > 0 ? (React.createElement(Button, { disabled: disabled, icon: "cross", minimal: true, onClick: this.handleClearButtonClick })) : undefined; | ||
return (React.createElement("div", { "aria-autocomplete": "list", "aria-controls": this.listboxId, ...popoverTargetProps, ...targetProps, "aria-expanded": isOpen, | ||
// Note that we must set FILL here in addition to TagInput to get the wrapper element to full width | ||
className: classNames(targetProps.className, popoverTargetProps.className, { | ||
[CoreClasses.FILL]: fill, | ||
}), | ||
// Normally, Popover2 would also need to attach its own `onKeyDown` handler via `targetProps`, | ||
// but in our case we fully manage that interaction and listen for key events to open/close | ||
// the popover, so we elide it from the DOM. | ||
onKeyDown: this.getTagInputKeyDownHandler(handleKeyDown), onKeyUp: this.getTagInputKeyUpHandler(handleKeyUp), ref: ref, role: "combobox" }, | ||
React.createElement(TagInput, { placeholder: placeholder, rightElement: maybeClearButton, ...tagInputProps, className: classNames(Classes.MULTISELECT, tagInputProps.className), inputRef: this.refHandlers.input, inputProps: inputProps, inputValue: listProps.query, onAdd: this.getTagInputAddHandler(listProps), onInputChange: listProps.handleQueryChange, onRemove: this.handleTagRemove, values: selectedItems.map(this.props.tagRenderer) }))); | ||
}; | ||
@@ -121,2 +144,7 @@ handleItemSelect = (item, evt) => { | ||
}; | ||
getTagInputAddHandler = (listProps) => (values, method) => { | ||
if (method === "paste") { | ||
listProps.handlePaste(values); | ||
} | ||
}; | ||
getTagInputKeyDownHandler = (handleQueryListKeyDown) => { | ||
@@ -154,3 +182,7 @@ return (e) => { | ||
}; | ||
handleClearButtonClick = () => { | ||
this.props.onClear?.(); | ||
this.refHandlers.popover.current?.reposition(); // reposition when size of input changes | ||
}; | ||
} | ||
//# sourceMappingURL=multiSelect2.js.map |
@@ -13,2 +13,6 @@ import * as React from "react"; | ||
/** | ||
* 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 | ||
@@ -15,0 +19,0 @@ * (up/down to navigate active item). This callback is passed to `renderer` and (along with |
@@ -187,3 +187,3 @@ /* | ||
renderItemList = (listProps) => { | ||
const { initialContent, noResults } = this.props; | ||
const { initialContent, noResults, menuProps } = this.props; | ||
// omit noResults if createNewItemFromQuery and createNewItemRenderer are both supplied, and query is not empty | ||
@@ -197,3 +197,3 @@ const createItemView = listProps.renderCreateItem(); | ||
const createFirst = this.isCreateItemFirst(); | ||
return (React.createElement(Menu, { ulRef: listProps.itemsParentRef }, | ||
return (React.createElement(Menu, { role: "listbox", ...menuProps, ulRef: listProps.itemsParentRef }, | ||
createFirst && createItemView, | ||
@@ -200,0 +200,0 @@ menuContent, |
import * as React from "react"; | ||
import { AbstractPureComponent2, InputGroupProps2 } from "@blueprintjs/core"; | ||
import { Popover2Props } from "@blueprintjs/popover2"; | ||
import { IListItemsProps } from "../../common"; | ||
export interface Select2Props<T> extends IListItemsProps<T> { | ||
import { IListItemsProps, SelectPopoverProps } from "../../common"; | ||
export interface Select2Props<T> extends IListItemsProps<T>, SelectPopoverProps { | ||
/** | ||
* Element which triggers the select popover. In most cases, you should display | ||
* the name or label of the curently selected item here. | ||
*/ | ||
children?: React.ReactNode; | ||
/** | ||
* Whether the component is non-interactive. | ||
* If true, the list's item renderer will not be called. | ||
* Note that you'll also need to disable the component's children, if appropriate. | ||
* | ||
* @default false | ||
*/ | ||
disabled?: boolean; | ||
/** | ||
* Whether the component should take up the full width of its container. | ||
@@ -21,10 +32,2 @@ * This overrides `popoverProps.fill`. You also have to ensure that the child | ||
/** | ||
* Whether the component is non-interactive. | ||
* If true, the list's item renderer will not be called. | ||
* Note that you'll also need to disable the component's children, if appropriate. | ||
* | ||
* @default false | ||
*/ | ||
disabled?: boolean; | ||
/** | ||
* Props to spread to the query `InputGroup`. Use `query` and | ||
@@ -35,5 +38,7 @@ * `onQueryChange` instead of `inputProps.value` and `inputProps.onChange` | ||
inputProps?: InputGroupProps2; | ||
/** Props to spread to `Popover2`. Note that `content` cannot be changed. */ | ||
popoverProps?: Partial<Omit<Popover2Props, "content">>; | ||
/** | ||
* Props to add to the popover target wrapper element. | ||
*/ | ||
popoverTargetProps?: React.HTMLAttributes<HTMLDivElement>; | ||
/** | ||
* Whether the active item should be reset to the first matching item _when | ||
@@ -53,4 +58,4 @@ * the popover closes_. The query will also be reset to the empty string. | ||
state: Select2State; | ||
inputElement: HTMLInputElement | null; | ||
private TypedQueryList; | ||
inputElement: HTMLInputElement | null; | ||
private queryList; | ||
@@ -60,6 +65,11 @@ private previousFocusedElement; | ||
private handleQueryListRef; | ||
private listboxId; | ||
render(): JSX.Element; | ||
componentDidUpdate(prevProps: Select2Props<T>, prevState: Select2State): void; | ||
private renderQueryList; | ||
private getPopoverTargetRenderer; | ||
private maybeRenderClearButton; | ||
/** | ||
* Target wrapper element "keydown" handler while the popover is closed. | ||
*/ | ||
private handleTargetKeyDown; | ||
@@ -66,0 +76,0 @@ private handleItemSelect; |
@@ -19,4 +19,4 @@ /* | ||
import * as React from "react"; | ||
import { AbstractPureComponent2, Button, DISPLAYNAME_PREFIX, InputGroup, Keys, Position, refHandler, setRef, } from "@blueprintjs/core"; | ||
import { Popover2 } from "@blueprintjs/popover2"; | ||
import { AbstractPureComponent2, Button, Classes as CoreClasses, DISPLAYNAME_PREFIX, InputGroup, Keys, Position, refHandler, setRef, Utils, } from "@blueprintjs/core"; | ||
import { Popover2, PopupKind } from "@blueprintjs/popover2"; | ||
import { Classes } from "../../common"; | ||
@@ -30,4 +30,4 @@ import { QueryList } from "../query-list/queryList"; | ||
state = { isOpen: false }; | ||
inputElement = null; | ||
TypedQueryList = QueryList.ofType(); | ||
inputElement = null; | ||
queryList = null; | ||
@@ -37,6 +37,7 @@ previousFocusedElement; | ||
handleQueryListRef = (ref) => (this.queryList = ref); | ||
listboxId = Utils.uniqueId("listbox"); | ||
render() { | ||
// omit props specific to this component, spread the rest. | ||
const { filterable, inputProps, popoverProps, ...restProps } = this.props; | ||
return (React.createElement(this.TypedQueryList, { ...restProps, onItemSelect: this.handleItemSelect, ref: this.handleQueryListRef, renderer: this.renderQueryList })); | ||
return (React.createElement(this.TypedQueryList, { ...restProps, menuProps: { id: this.listboxId }, onItemSelect: this.handleItemSelect, ref: this.handleQueryListRef, renderer: this.renderQueryList })); | ||
} | ||
@@ -55,20 +56,42 @@ componentDidUpdate(prevProps, prevState) { | ||
// not using defaultProps cuz they're hard to type with generics (can't use <T> on static members) | ||
const { fill, filterable = true, disabled = false, inputProps = {}, popoverProps = {} } = this.props; | ||
const { fill, filterable = true, disabled = false, inputProps = {}, popoverContentProps = {}, popoverProps = {}, popoverRef, } = this.props; | ||
if (fill) { | ||
popoverProps.fill = true; | ||
} | ||
const input = (React.createElement(InputGroup, { leftIcon: "search", placeholder: "Filter...", rightElement: this.maybeRenderClearButton(listProps.query), ...inputProps, inputRef: this.handleInputRef, onChange: listProps.handleQueryChange, value: listProps.query })); | ||
const input = (React.createElement(InputGroup, { "aria-autocomplete": "list", leftIcon: "search", placeholder: "Filter...", rightElement: this.maybeRenderClearButton(listProps.query), ...inputProps, inputRef: this.handleInputRef, onChange: listProps.handleQueryChange, value: listProps.query })); | ||
const { handleKeyDown, handleKeyUp } = listProps; | ||
return (React.createElement(Popover2, { autoFocus: false, enforceFocus: false, isOpen: this.state.isOpen, disabled: disabled, position: Position.BOTTOM_LEFT, ...popoverProps, className: classNames(listProps.className, popoverProps.className), content: React.createElement("div", { onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }, | ||
return (React.createElement(Popover2, { autoFocus: false, enforceFocus: false, isOpen: this.state.isOpen, disabled: disabled, position: Position.BOTTOM_LEFT, ...popoverProps, className: classNames(listProps.className, popoverProps.className), content: React.createElement("div", { ...popoverContentProps, onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }, | ||
filterable ? input : undefined, | ||
listProps.itemList), onInteraction: this.handlePopoverInteraction, popoverClassName: classNames(Classes.SELECT_POPOVER, popoverProps.popoverClassName), onOpening: this.handlePopoverOpening, onOpened: this.handlePopoverOpened, onClosing: this.handlePopoverClosing }, | ||
React.createElement("div", { onKeyDown: this.state.isOpen ? handleKeyDown : this.handleTargetKeyDown, onKeyUp: this.state.isOpen ? handleKeyUp : undefined }, this.props.children))); | ||
listProps.itemList), onClosing: this.handlePopoverClosing, onInteraction: this.handlePopoverInteraction, onOpened: this.handlePopoverOpened, onOpening: this.handlePopoverOpening, popoverClassName: classNames(Classes.SELECT_POPOVER, popoverProps.popoverClassName), popupKind: PopupKind.LISTBOX, ref: popoverRef, renderTarget: this.getPopoverTargetRenderer(listProps, this.state.isOpen) })); | ||
}; | ||
// We use the renderTarget API to flatten the rendered DOM and make it easier to implement features like | ||
// the "fill" prop. Note that we must take `isOpen` as an argument to force this render function to be called | ||
// again after that state changes. | ||
getPopoverTargetRenderer = (listProps, isOpen) => | ||
// N.B. pull out `isOpen` so that it's not forwarded to the DOM, but remember not to use it directly | ||
// since it may be stale (`renderTarget` is not re-invoked on this.state changes). | ||
// eslint-disable-next-line react/display-name | ||
({ isOpen: _isOpen, ref, ...targetProps }) => { | ||
const { popoverTargetProps } = this.props; | ||
const { handleKeyDown, handleKeyUp } = listProps; | ||
return (React.createElement("div", { "aria-controls": this.listboxId, ...popoverTargetProps, ...targetProps, "aria-expanded": isOpen, | ||
// Note that we must set FILL here in addition to children to get the wrapper element to full width | ||
className: classNames(targetProps.className, popoverTargetProps?.className, { | ||
[CoreClasses.FILL]: this.props.fill, | ||
}), | ||
// Normally, Popover2 would also need to attach its own `onKeyDown` handler via `targetProps`, | ||
// but in our case we fully manage that interaction and listen for key events to open/close | ||
// the popover, so we elide it from the DOM. | ||
onKeyDown: isOpen ? handleKeyDown : this.handleTargetKeyDown, onKeyUp: isOpen ? handleKeyUp : undefined, ref: ref, role: "combobox" }, this.props.children)); | ||
}; | ||
maybeRenderClearButton(query) { | ||
return query.length > 0 ? React.createElement(Button, { icon: "cross", minimal: true, onClick: this.resetQuery }) : undefined; | ||
} | ||
/** | ||
* Target wrapper element "keydown" handler while the popover is closed. | ||
*/ | ||
handleTargetKeyDown = (event) => { | ||
// open popover when arrow key pressed on target while closed | ||
// HACKHACK: https://github.com/palantir/blueprint/issues/4165 | ||
// eslint-disable-next-line deprecation/deprecation | ||
/* eslint-disable deprecation/deprecation */ | ||
if (event.which === Keys.ARROW_UP || event.which === Keys.ARROW_DOWN) { | ||
@@ -78,2 +101,6 @@ event.preventDefault(); | ||
} | ||
else if (Keys.isKeyboardClick(event.keyCode)) { | ||
this.setState({ isOpen: true }); | ||
} | ||
/* eslint-enable deprecation/deprecation */ | ||
}; | ||
@@ -80,0 +107,0 @@ handleItemSelect = (item, event) => { |
/// <reference types="react" /> | ||
import { AbstractPureComponent2, InputGroupProps2 } from "@blueprintjs/core"; | ||
import { Popover2Props } from "@blueprintjs/popover2"; | ||
import { IListItemsProps } from "../../common"; | ||
export interface Suggest2Props<T> extends IListItemsProps<T> { | ||
import { IListItemsProps, SelectPopoverProps } from "../../common"; | ||
export interface Suggest2Props<T> extends IListItemsProps<T>, SelectPopoverProps { | ||
/** | ||
@@ -48,4 +47,2 @@ * Whether the popover should close after selecting an item. | ||
openOnKeyDown?: boolean; | ||
/** Props to spread to `Popover2`. Note that `content` cannot be changed. */ | ||
popoverProps?: Partial<Omit<Popover2Props, "content">>; | ||
/** | ||
@@ -73,5 +70,7 @@ * Whether the active item should be reset to the first matching item _when | ||
private handleQueryListRef; | ||
private listboxId; | ||
render(): JSX.Element; | ||
componentDidUpdate(prevProps: Suggest2Props<T>, prevState: Suggest2State<T>): void; | ||
private renderQueryList; | ||
private getPopoverTargetRenderer; | ||
private selectText; | ||
@@ -78,0 +77,0 @@ private handleInputFocus; |
@@ -19,4 +19,4 @@ /* | ||
import * as React from "react"; | ||
import { AbstractPureComponent2, DISPLAYNAME_PREFIX, InputGroup, Keys, Position, refHandler, setRef, } from "@blueprintjs/core"; | ||
import { Popover2 } from "@blueprintjs/popover2"; | ||
import { AbstractPureComponent2, DISPLAYNAME_PREFIX, InputGroup, Keys, mergeRefs, Position, refHandler, setRef, Utils, } from "@blueprintjs/core"; | ||
import { Popover2, PopupKind } from "@blueprintjs/popover2"; | ||
import { Classes } from "../../common"; | ||
@@ -44,6 +44,7 @@ import { QueryList } from "../query-list/queryList"; | ||
handleQueryListRef = (ref) => (this.queryList = ref); | ||
listboxId = Utils.uniqueId("listbox"); | ||
render() { | ||
// omit props specific to this component, spread the rest. | ||
const { disabled, inputProps, popoverProps, ...restProps } = this.props; | ||
return (React.createElement(this.TypedQueryList, { ...restProps, initialActiveItem: this.props.selectedItem ?? undefined, onItemSelect: this.handleItemSelect, ref: this.handleQueryListRef, renderer: this.renderQueryList })); | ||
return (React.createElement(this.TypedQueryList, { ...restProps, menuProps: { id: this.listboxId }, initialActiveItem: this.props.selectedItem ?? undefined, onItemSelect: this.handleItemSelect, ref: this.handleQueryListRef, renderer: this.renderQueryList })); | ||
} | ||
@@ -72,7 +73,23 @@ componentDidUpdate(prevProps, prevState) { | ||
renderQueryList = (listProps) => { | ||
const { fill, inputProps = {}, popoverProps = {} } = this.props; | ||
const { isOpen, selectedItem } = this.state; | ||
const { popoverContentProps = {}, popoverProps = {}, popoverRef } = this.props; | ||
const { isOpen } = this.state; | ||
const { handleKeyDown, handleKeyUp } = listProps; | ||
// N.B. no need to set `popoverProps.fill` since that is unused with the `renderTarget` API | ||
return (React.createElement(Popover2, { autoFocus: false, enforceFocus: false, isOpen: isOpen, position: Position.BOTTOM_LEFT, ...popoverProps, className: classNames(listProps.className, popoverProps.className), content: React.createElement("div", { ...popoverContentProps, onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }, listProps.itemList), interactionKind: "click", onInteraction: this.handlePopoverInteraction, onOpened: this.handlePopoverOpened, onOpening: this.handlePopoverOpening, popoverClassName: classNames(Classes.SELECT_POPOVER, popoverProps.popoverClassName), popupKind: PopupKind.LISTBOX, ref: popoverRef, renderTarget: this.getPopoverTargetRenderer(listProps, isOpen) })); | ||
}; | ||
// We use the renderTarget API to flatten the rendered DOM and make it easier to implement features like | ||
// the "fill" prop. Note that we must take `isOpen` as an argument to force this render function to be called | ||
// again after that state changes. | ||
getPopoverTargetRenderer = (listProps, isOpen) => | ||
// eslint-disable-next-line react/display-name | ||
({ | ||
// pull out `isOpen` so that it's not forwarded to the DOM | ||
isOpen: _isOpen, | ||
// pull out `defaultValue` due to type incompatibility with InputGroup. | ||
defaultValue, ref, ...targetProps }) => { | ||
const { disabled, fill, inputProps = {}, inputValueRenderer, resetOnClose } = this.props; | ||
const { selectedItem } = this.state; | ||
const { handleKeyDown, handleKeyUp } = listProps; | ||
const selectedItemText = selectedItem == null ? "" : inputValueRenderer(selectedItem); | ||
const { autoComplete = "off", placeholder = "Search..." } = inputProps; | ||
const selectedItemText = selectedItem ? this.props.inputValueRenderer(selectedItem) : ""; | ||
// placeholder shows selected item while open. | ||
@@ -82,11 +99,4 @@ const inputPlaceholder = isOpen && selectedItemText ? selectedItemText : placeholder; | ||
// if resetOnClose is enabled, then hide query when not open. (see handlePopoverOpening) | ||
const inputValue = isOpen | ||
? listProps.query | ||
: selectedItemText || (this.props.resetOnClose ? "" : listProps.query); | ||
if (fill) { | ||
popoverProps.fill = true; | ||
inputProps.fill = true; | ||
} | ||
return (React.createElement(Popover2, { autoFocus: false, enforceFocus: false, isOpen: isOpen, position: Position.BOTTOM_LEFT, ...popoverProps, className: classNames(listProps.className, popoverProps.className), content: React.createElement("div", { onKeyDown: handleKeyDown, onKeyUp: handleKeyUp }, listProps.itemList), interactionKind: "click", onInteraction: this.handlePopoverInteraction, popoverClassName: classNames(Classes.SELECT_POPOVER, popoverProps.popoverClassName), onOpening: this.handlePopoverOpening, onOpened: this.handlePopoverOpened }, | ||
React.createElement(InputGroup, { autoComplete: autoComplete, disabled: this.props.disabled, ...inputProps, inputRef: this.handleInputRef, onChange: listProps.handleQueryChange, onFocus: this.handleInputFocus, onKeyDown: this.getTargetKeyDownHandler(handleKeyDown), onKeyUp: this.getTargetKeyUpHandler(handleKeyUp), placeholder: inputPlaceholder, value: inputValue }))); | ||
const inputValue = isOpen ? listProps.query : selectedItemText ?? (resetOnClose ? "" : listProps.query); | ||
return (React.createElement(InputGroup, { autoComplete: autoComplete, disabled: disabled, "aria-controls": this.listboxId, ...targetProps, ...inputProps, "aria-autocomplete": "list", "aria-expanded": isOpen, fill: fill, inputRef: mergeRefs(this.handleInputRef, ref), onChange: listProps.handleQueryChange, onFocus: this.handleInputFocus, onKeyDown: this.getTargetKeyDownHandler(handleKeyDown), onKeyUp: this.getTargetKeyUpHandler(handleKeyUp), placeholder: inputPlaceholder, role: "combobox", value: inputValue })); | ||
}; | ||
@@ -93,0 +103,0 @@ selectText = () => { |
{ | ||
"name": "@blueprintjs/select", | ||
"version": "4.3.1", | ||
"version": "4.4.0", | ||
"description": "Components related to selecting items from a list", | ||
@@ -38,4 +38,4 @@ "main": "lib/cjs/index.js", | ||
"dependencies": { | ||
"@blueprintjs/core": "^4.4.1", | ||
"@blueprintjs/popover2": "^1.3.1", | ||
"@blueprintjs/core": "^4.5.0", | ||
"@blueprintjs/popover2": "^1.4.0", | ||
"classnames": "^2.2", | ||
@@ -42,0 +42,0 @@ "tslib": "~2.3.1" |
@@ -25,1 +25,2 @@ /* | ||
export * from "./predicate"; | ||
export type { SelectPopoverProps } from "./selectPopoverProps"; |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
885141
202
12108
Updated@blueprintjs/core@^4.5.0
Updated@blueprintjs/popover2@^1.4.0