@grafana/ui
Version:
Grafana Components Library
205 lines (198 loc) • 7.56 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var jsxRuntime = require('react/jsx-runtime');
var css = require('@emotion/css');
var lodash = require('lodash');
var React = require('react');
var ReactDOM = require('react-dom');
var reactWindow = require('react-window');
var data = require('@grafana/data');
var completion = require('../../types/completion.cjs');
var typeahead = require('../../utils/typeahead.cjs');
var TypeaheadInfo = require('./TypeaheadInfo.cjs');
var TypeaheadItem = require('./TypeaheadItem.cjs');
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e }; }
var ReactDOM__default = /*#__PURE__*/_interopDefaultCompat(ReactDOM);
"use strict";
const modulo = (a, n) => a - n * Math.floor(a / n);
class Typeahead extends React.PureComponent {
constructor() {
super(...arguments);
this.listRef = React.createRef();
this.state = {
hoveredItem: null,
typeaheadIndex: null,
allItems: [],
listWidth: -1,
listHeight: -1,
itemHeight: -1
};
this.componentDidMount = () => {
if (this.props.menuRef) {
this.props.menuRef(this);
}
document.addEventListener("selectionchange", this.handleSelectionChange);
const allItems = typeahead.flattenGroupItems(this.props.groupedItems);
const longestLabel = typeahead.calculateLongestLabel(allItems);
const { listWidth, listHeight, itemHeight } = typeahead.calculateListSizes(this.context, allItems, longestLabel);
this.setState({
listWidth,
listHeight,
itemHeight,
allItems
});
};
this.componentWillUnmount = () => {
document.removeEventListener("selectionchange", this.handleSelectionChange);
};
this.handleSelectionChange = () => {
this.forceUpdate();
};
this.componentDidUpdate = (prevProps, prevState) => {
if (this.state.typeaheadIndex !== null && prevState.typeaheadIndex !== this.state.typeaheadIndex && this.listRef && this.listRef.current) {
if (this.state.typeaheadIndex === 1) {
this.listRef.current.scrollToItem(0);
return;
}
this.listRef.current.scrollToItem(this.state.typeaheadIndex);
}
if (lodash.isEqual(prevProps.groupedItems, this.props.groupedItems) === false) {
const allItems = typeahead.flattenGroupItems(this.props.groupedItems);
const longestLabel = typeahead.calculateLongestLabel(allItems);
const { listWidth, listHeight, itemHeight } = typeahead.calculateListSizes(this.context, allItems, longestLabel);
this.setState({ listWidth, listHeight, itemHeight, allItems, typeaheadIndex: null });
}
};
this.onMouseEnter = (index) => {
this.setState({
hoveredItem: index
});
};
this.onMouseLeave = () => {
this.setState({
hoveredItem: null
});
};
this.moveMenuIndex = (moveAmount) => {
const itemCount = this.state.allItems.length;
if (itemCount) {
const typeaheadIndex = this.state.typeaheadIndex || 0;
let newTypeaheadIndex = modulo(typeaheadIndex + moveAmount, itemCount);
if (this.state.allItems[newTypeaheadIndex].kind === completion.CompletionItemKind.GroupTitle) {
newTypeaheadIndex = modulo(newTypeaheadIndex + moveAmount, itemCount);
}
this.setState({
typeaheadIndex: newTypeaheadIndex
});
return;
}
};
this.insertSuggestion = () => {
if (this.props.onSelectSuggestion && this.state.typeaheadIndex !== null) {
this.props.onSelectSuggestion(this.state.allItems[this.state.typeaheadIndex]);
}
};
}
get menuPosition() {
if (!window.getSelection) {
return "";
}
const selection = window.getSelection();
const node = selection && selection.anchorNode;
if (node && node.parentElement) {
const rect = node.parentElement.getBoundingClientRect();
const scrollX = window.scrollX;
const scrollY = window.scrollY;
return `position: absolute; display: flex; top: ${rect.top + scrollY + rect.height + 6}px; left: ${rect.left + scrollX - 2}px`;
}
return "";
}
render() {
const { prefix, isOpen = false, origin } = this.props;
const { allItems, listWidth, listHeight, itemHeight, hoveredItem, typeaheadIndex } = this.state;
const styles = getStyles(this.context);
const showDocumentation = hoveredItem || typeaheadIndex;
const documentationItem = allItems[hoveredItem ? hoveredItem : typeaheadIndex || 0];
return /* @__PURE__ */ jsxRuntime.jsxs(Portal, { origin, isOpen, style: this.menuPosition, children: [
/* @__PURE__ */ jsxRuntime.jsx("ul", { role: "menu", className: styles.typeahead, "data-testid": "typeahead", children: /* @__PURE__ */ jsxRuntime.jsx(
reactWindow.FixedSizeList,
{
ref: this.listRef,
itemCount: allItems.length,
itemSize: itemHeight,
itemKey: (index) => {
const item = allItems && allItems[index];
const key = item ? `${index}-${item.label}` : `${index}`;
return key;
},
width: listWidth,
height: listHeight,
children: ({ index, style }) => {
const item = allItems && allItems[index];
if (!item) {
return null;
}
return /* @__PURE__ */ jsxRuntime.jsx(
TypeaheadItem.TypeaheadItem,
{
onClickItem: () => this.props.onSelectSuggestion ? this.props.onSelectSuggestion(item) : {},
isSelected: typeaheadIndex === null ? false : allItems[typeaheadIndex] === item,
item,
prefix,
style,
onMouseEnter: () => this.onMouseEnter(index),
onMouseLeave: this.onMouseLeave
}
);
}
}
) }),
showDocumentation && /* @__PURE__ */ jsxRuntime.jsx(TypeaheadInfo.TypeaheadInfo, { height: listHeight, item: documentationItem })
] });
}
}
Typeahead.contextType = data.ThemeContext;
class Portal extends React.PureComponent {
constructor(props) {
super(props);
const { index = 0, origin = "query", style } = props;
this.node = document.createElement("div");
this.node.setAttribute("style", style);
this.node.classList.add(`slate-typeahead-${origin}-${index}`);
document.body.appendChild(this.node);
}
componentWillUnmount() {
document.body.removeChild(this.node);
}
render() {
if (this.props.isOpen) {
this.node.setAttribute("style", this.props.style);
this.node.classList.add(`slate-typeahead--open`);
return ReactDOM__default.default.createPortal(this.props.children, this.node);
} else {
this.node.classList.remove(`slate-typeahead--open`);
}
return null;
}
}
const getStyles = (theme) => ({
typeahead: css.css({
position: "relative",
zIndex: theme.zIndex.typeahead,
borderRadius: theme.shape.radius.default,
border: `1px solid ${theme.components.panel.borderColor}`,
maxHeight: "66vh",
overflowY: "scroll",
overflowX: "hidden",
outline: "none",
listStyle: "none",
background: theme.components.panel.background,
color: theme.colors.text.primary,
boxShadow: theme.shadows.z2,
strong: {
color: theme.v1.palette.yellow
}
})
});
exports.Typeahead = Typeahead;
//# sourceMappingURL=Typeahead.cjs.map