Commit 538ea112 by David Kaltschmidt

Perf on query field and typeahead

- Lower debounce
- refactor suggestion selection
- make Portal pure
- use prev suggestions object if suggestions are the same
parent 9914071c
...@@ -575,6 +575,7 @@ class PromQueryField extends React.Component<PromQueryFieldProps, PromQueryField ...@@ -575,6 +575,7 @@ class PromQueryField extends React.Component<PromQueryFieldProps, PromQueryField
onWillApplySuggestion={willApplySuggestion} onWillApplySuggestion={willApplySuggestion}
onValueChanged={this.onChangeQuery} onValueChanged={this.onChangeQuery}
placeholder="Enter a PromQL query" placeholder="Enter a PromQL query"
portalPrefix="prometheus"
/> />
</div> </div>
{error ? <div className="prom-query-field-info text-error">{error}</div> : null} {error ? <div className="prom-query-field-info text-error">{error}</div> : null}
......
...@@ -11,10 +11,17 @@ import NewlinePlugin from './slate-plugins/newline'; ...@@ -11,10 +11,17 @@ import NewlinePlugin from './slate-plugins/newline';
import Typeahead from './Typeahead'; import Typeahead from './Typeahead';
import { makeFragment, makeValue } from './Value'; import { makeFragment, makeValue } from './Value';
export const TYPEAHEAD_DEBOUNCE = 300; export const TYPEAHEAD_DEBOUNCE = 100;
function flattenSuggestions(s: any[]): any[] { function getSuggestionByIndex(suggestions: SuggestionGroup[], index: number): Suggestion {
return s ? s.reduce((acc, g) => acc.concat(g.items), []) : []; // Flatten suggestion groups
const flattenedSuggestions = suggestions.reduce((acc, g) => acc.concat(g.items), []);
const correctedIndex = Math.max(index, 0) % flattenedSuggestions.length;
return flattenedSuggestions[correctedIndex];
}
function hasSuggestions(suggestions: SuggestionGroup[]): boolean {
return suggestions && suggestions.length > 0;
} }
export interface Suggestion { export interface Suggestion {
...@@ -154,8 +161,14 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat ...@@ -154,8 +161,14 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat
clearTimeout(this.resetTimer); clearTimeout(this.resetTimer);
} }
componentDidUpdate() { componentDidUpdate(prevProps, prevState) {
this.updateMenu(); // Only update menu location when suggestion existence or text/selection changed
if (
this.state.value !== prevState.value ||
hasSuggestions(this.state.suggestions) !== hasSuggestions(prevState.suggestions)
) {
this.updateMenu();
}
} }
componentWillReceiveProps(nextProps) { componentWillReceiveProps(nextProps) {
...@@ -216,7 +229,7 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat ...@@ -216,7 +229,7 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat
wrapperNode, wrapperNode,
}); });
const filteredSuggestions = suggestions let filteredSuggestions = suggestions
.map(group => { .map(group => {
if (group.items) { if (group.items) {
if (prefix) { if (prefix) {
...@@ -241,6 +254,11 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat ...@@ -241,6 +254,11 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat
}) })
.filter(group => group.items && group.items.length > 0); // Filter out empty groups .filter(group => group.items && group.items.length > 0); // Filter out empty groups
// Keep same object for equality checking later
if (_.isEqual(filteredSuggestions, this.state.suggestions)) {
filteredSuggestions = this.state.suggestions;
}
this.setState( this.setState(
{ {
suggestions: filteredSuggestions, suggestions: filteredSuggestions,
...@@ -326,12 +344,7 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat ...@@ -326,12 +344,7 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat
return undefined; return undefined;
} }
// Get the currently selected suggestion const suggestion = getSuggestionByIndex(suggestions, typeaheadIndex);
const flattenedSuggestions = flattenSuggestions(suggestions);
const selected = Math.abs(typeaheadIndex);
const selectedIndex = selected % flattenedSuggestions.length || 0;
const suggestion = flattenedSuggestions[selectedIndex];
this.applyTypeahead(change, suggestion); this.applyTypeahead(change, suggestion);
return true; return true;
} }
...@@ -408,8 +421,7 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat ...@@ -408,8 +421,7 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat
} }
// No suggestions or blur, remove menu // No suggestions or blur, remove menu
const hasSuggesstions = suggestions && suggestions.length > 0; if (!hasSuggestions(suggestions)) {
if (!hasSuggesstions) {
menu.removeAttribute('style'); menu.removeAttribute('style');
return; return;
} }
...@@ -436,18 +448,12 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat ...@@ -436,18 +448,12 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat
renderMenu = () => { renderMenu = () => {
const { portalPrefix } = this.props; const { portalPrefix } = this.props;
const { suggestions } = this.state; const { suggestions, typeaheadIndex } = this.state;
const hasSuggesstions = suggestions && suggestions.length > 0; if (!hasSuggestions(suggestions)) {
if (!hasSuggesstions) {
return null; return null;
} }
// Guard selectedIndex to be within the length of the suggestions const selectedItem = getSuggestionByIndex(suggestions, typeaheadIndex);
let selectedIndex = Math.max(this.state.typeaheadIndex, 0);
const flattenedSuggestions = flattenSuggestions(suggestions);
selectedIndex = selectedIndex % flattenedSuggestions.length || 0;
const selectedItem: Suggestion | null =
flattenedSuggestions.length > 0 ? flattenedSuggestions[selectedIndex] : null;
// Create typeahead in DOM root so we can later position it absolutely // Create typeahead in DOM root so we can later position it absolutely
return ( return (
...@@ -482,7 +488,7 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat ...@@ -482,7 +488,7 @@ class QueryField extends React.Component<TypeaheadFieldProps, TypeaheadFieldStat
} }
} }
class Portal extends React.Component<{ index?: number; prefix: string }, {}> { class Portal extends React.PureComponent<{ index?: number; prefix: string }, {}> {
node: HTMLElement; node: HTMLElement;
constructor(props) { constructor(props) {
......
...@@ -23,7 +23,9 @@ class TypeaheadItem extends React.PureComponent<TypeaheadItemProps, {}> { ...@@ -23,7 +23,9 @@ class TypeaheadItem extends React.PureComponent<TypeaheadItemProps, {}> {
componentDidUpdate(prevProps) { componentDidUpdate(prevProps) {
if (this.props.isSelected && !prevProps.isSelected) { if (this.props.isSelected && !prevProps.isSelected) {
scrollIntoView(this.el); requestAnimationFrame(() => {
scrollIntoView(this.el);
});
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment