azure-devops-ui
Version:
React components for building web UI in Azure DevOps
92 lines (91 loc) • 6.24 kB
JavaScript
import "../../CommonImports";
import "../../Core/core.css";
import "./SuggestionsList.css";
import * as React from "react";
import { ObservableLike } from '../../Core/Observable';
import { FocusWithin } from '../../FocusWithin';
import { Observer } from '../../Observer';
import { Spinner } from '../../Spinner';
import { css, getSafeId, preventDefault } from '../../Util';
export class SuggestionsItem extends React.Component {
constructor() {
super(...arguments);
this.onSuggestionClicked = (ev) => {
if (!ev.isDefaultPrevented()) {
this.props.onClick(this.props);
ev.preventDefault();
}
};
}
render() {
const { renderSuggestion, className, isSelected } = this.props;
return (React.createElement("div", { className: css(className, "bolt-suggestions-item flex-row flex-grow cursor-pointer scroll-hidden", isSelected && "bolt-suggestions-isSuggested"), onPointerDown: preventDefault },
React.createElement("div", { onMouseDown: preventDefault, onClick: this.onSuggestionClicked, className: "bolt-suggestions-item-button flex-row flex-grow scroll-hidden" }, renderSuggestion(this.props))));
}
}
export class SuggestionsList extends React.Component {
constructor() {
super(...arguments);
this.scrollableRegion = React.createRef();
this.selectedElement = React.createRef();
this.onBlur = () => {
this.props.onBlur && this.props.onBlur();
};
this.onFocus = (event) => {
this.props.onFocus && this.props.onFocus(event);
};
}
render() {
const { className, loadingText, renderNoResultFound } = this.props;
const suggestions = ObservableLike.getValue(this.props.suggestions);
const isLoading = ObservableLike.getValue(this.props.isLoading);
// MostRecently Used text should supercede the header text if it's there and available.
const hasNoSuggestions = (!suggestions || !suggestions.length) && !isLoading;
return (React.createElement("div", { className: css(className, "bolt-suggestions flex-row flex-grow"), id: getSafeId("suggestions-list"), style: this.props.width ? { width: this.props.width } : undefined, onMouseDown: preventDefault },
isLoading ? (React.createElement(Spinner, { className: "bolt-suggestions-spinner flex-grow flex-center justify-center", id: "suggestions-spinner", label: loadingText })) : (!hasNoSuggestions && this.renderSuggestions()),
React.createElement("div", { className: css("bolt-suggestions-none-region", !hasNoSuggestions ? "has-suggestions" : "flex-row flex-grow"), id: getSafeId("sug-list-no-results"), role: "alert" }, hasNoSuggestions && (renderNoResultFound ? renderNoResultFound() : this.noResults())),
React.createElement("div", { "aria-label": loadingText || "Loading", id: getSafeId("sug-list-transition"), role: "text" })));
}
componentDidMount() {
this.scrollSelected();
}
componentDidUpdate() {
// Scroll to selected element only in case if selected element changed.
// Otherwise mouse scroll won't work as it doesn't change selected element.
if (this.selectedElement.current && this.currentSelectedElement !== this.selectedElement.current) {
this.scrollSelected();
}
}
noResults() {
const { noResultsFoundText } = this.props;
return noResultsFoundText ? React.createElement("div", { className: "bolt-suggestions-none flex-row flex-grow flex-center" }, noResultsFoundText) : null;
}
renderSuggestions() {
const { createDefaultItem, renderSuggestion, onSuggestionClicked, suggestionsItemClassName, resultsMaximumNumber, selectedIndex, suggestions, suggestionsContainerAriaLabel, width } = this.props;
return (React.createElement(FocusWithin, { onBlur: this.onBlur, onFocus: this.onFocus }, (focusStatus) => {
return (React.createElement(Observer, { suggestions: suggestions, selectedIndex: selectedIndex }, (props) => {
if (resultsMaximumNumber) {
props.suggestions = props.suggestions.slice(0, resultsMaximumNumber);
}
const sugList = props.suggestions;
createDefaultItem && sugList.unshift(createDefaultItem(sugList));
return (React.createElement("div", { "aria-label": suggestionsContainerAriaLabel, className: "bolt-suggestions-container flex-column flex-grow v-scroll-auto h-scroll-hidden", id: getSafeId("suggestion-list"), ref: this.scrollableRegion, role: "listbox", style: width ? { width: width } : undefined },
React.createElement("div", { className: "bolt-suggestion-spacer", onMouseDown: preventDefault }),
props.suggestions.map((suggestion, index) => {
const isSuggested = index === props.selectedIndex;
return (React.createElement("div", { className: "flex-noshrink", key: getSafeId("sug-" + index), id: getSafeId("sug-row-" + index), ref: isSuggested ? this.selectedElement : "", role: "option" },
React.createElement(SuggestionsItem, { className: suggestionsItemClassName, id: getSafeId("sug-item" + index), index: index, isSelected: isSuggested, item: suggestion, onBlur: focusStatus.onBlur, onFocus: focusStatus.onFocus, onClick: onSuggestionClicked, renderSuggestion: renderSuggestion })));
}),
React.createElement("div", { className: "bolt-suggestion-spacer", onMouseDown: preventDefault })));
}));
}));
}
scrollSelected() {
var _a, _b;
this.currentSelectedElement = (_a = this.selectedElement) === null || _a === void 0 ? void 0 : _a.current;
if (this.scrollableRegion.current && ((_b = this.selectedElement) === null || _b === void 0 ? void 0 : _b.current)) {
// IE11 messes up when scrollIntoView is called in the callout.
this.scrollableRegion.current.scrollTop = this.selectedElement.current.offsetTop;
}
}
}