UNPKG

@grafana/ui

Version:
1 lines 15.2 kB
{"version":3,"file":"Typeahead.mjs","sources":["../../../../src/components/Typeahead/Typeahead.tsx"],"sourcesContent":["import { css } from '@emotion/css';\nimport { isEqual } from 'lodash';\nimport { createRef, PureComponent } from 'react';\nimport * as React from 'react';\nimport ReactDOM from 'react-dom';\nimport { FixedSizeList } from 'react-window';\n\nimport { GrafanaTheme2, ThemeContext } from '@grafana/data';\n\nimport { CompletionItem, CompletionItemGroup, CompletionItemKind } from '../../types/completion';\nimport { flattenGroupItems, calculateLongestLabel, calculateListSizes } from '../../utils/typeahead';\n\nimport { TypeaheadInfo } from './TypeaheadInfo';\nimport { TypeaheadItem } from './TypeaheadItem';\n\nconst modulo = (a: number, n: number) => a - n * Math.floor(a / n);\n\ninterface Props {\n origin: string;\n groupedItems: CompletionItemGroup[];\n prefix?: string;\n menuRef?: (el: Typeahead) => void;\n onSelectSuggestion?: (suggestion: CompletionItem) => void;\n isOpen?: boolean;\n}\n\nexport interface State {\n allItems: CompletionItem[];\n listWidth: number;\n listHeight: number;\n itemHeight: number;\n hoveredItem: number | null;\n typeaheadIndex: number | null;\n}\n\nexport class Typeahead extends PureComponent<Props, State> {\n static contextType = ThemeContext;\n context!: React.ContextType<typeof ThemeContext>;\n listRef = createRef<FixedSizeList>();\n\n state: State = {\n hoveredItem: null,\n typeaheadIndex: null,\n allItems: [],\n listWidth: -1,\n listHeight: -1,\n itemHeight: -1,\n };\n\n componentDidMount = () => {\n if (this.props.menuRef) {\n this.props.menuRef(this);\n }\n\n document.addEventListener('selectionchange', this.handleSelectionChange);\n\n const allItems = flattenGroupItems(this.props.groupedItems);\n const longestLabel = calculateLongestLabel(allItems);\n const { listWidth, listHeight, itemHeight } = calculateListSizes(this.context, allItems, longestLabel);\n this.setState({\n listWidth,\n listHeight,\n itemHeight,\n allItems,\n });\n };\n\n componentWillUnmount = () => {\n document.removeEventListener('selectionchange', this.handleSelectionChange);\n };\n\n handleSelectionChange = () => {\n this.forceUpdate();\n };\n\n componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>) => {\n if (\n this.state.typeaheadIndex !== null &&\n prevState.typeaheadIndex !== this.state.typeaheadIndex &&\n this.listRef &&\n this.listRef.current\n ) {\n if (this.state.typeaheadIndex === 1) {\n this.listRef.current.scrollToItem(0); // special case for handling the first group label\n return;\n }\n this.listRef.current.scrollToItem(this.state.typeaheadIndex);\n }\n\n if (isEqual(prevProps.groupedItems, this.props.groupedItems) === false) {\n const allItems = flattenGroupItems(this.props.groupedItems);\n const longestLabel = calculateLongestLabel(allItems);\n const { listWidth, listHeight, itemHeight } = calculateListSizes(this.context, allItems, longestLabel);\n this.setState({ listWidth, listHeight, itemHeight, allItems, typeaheadIndex: null });\n }\n };\n\n onMouseEnter = (index: number) => {\n this.setState({\n hoveredItem: index,\n });\n };\n\n onMouseLeave = () => {\n this.setState({\n hoveredItem: null,\n });\n };\n\n moveMenuIndex = (moveAmount: number) => {\n const itemCount = this.state.allItems.length;\n if (itemCount) {\n // Select next suggestion\n const typeaheadIndex = this.state.typeaheadIndex || 0;\n let newTypeaheadIndex = modulo(typeaheadIndex + moveAmount, itemCount);\n\n if (this.state.allItems[newTypeaheadIndex].kind === CompletionItemKind.GroupTitle) {\n newTypeaheadIndex = modulo(newTypeaheadIndex + moveAmount, itemCount);\n }\n\n this.setState({\n typeaheadIndex: newTypeaheadIndex,\n });\n\n return;\n }\n };\n\n insertSuggestion = () => {\n if (this.props.onSelectSuggestion && this.state.typeaheadIndex !== null) {\n this.props.onSelectSuggestion(this.state.allItems[this.state.typeaheadIndex]);\n }\n };\n\n get menuPosition(): string {\n // Exit for unit tests\n if (!window.getSelection) {\n return '';\n }\n\n const selection = window.getSelection();\n const node = selection && selection.anchorNode;\n\n // Align menu overlay to editor node\n if (node && node.parentElement) {\n // Read from DOM\n const rect = node.parentElement.getBoundingClientRect();\n const scrollX = window.scrollX;\n const scrollY = window.scrollY;\n\n return `position: absolute; display: flex; top: ${rect.top + scrollY + rect.height + 6}px; left: ${\n rect.left + scrollX - 2\n }px`;\n }\n\n return '';\n }\n\n render() {\n const { prefix, isOpen = false, origin } = this.props;\n const { allItems, listWidth, listHeight, itemHeight, hoveredItem, typeaheadIndex } = this.state;\n const styles = getStyles(this.context);\n\n const showDocumentation = hoveredItem || typeaheadIndex;\n const documentationItem = allItems[hoveredItem ? hoveredItem : typeaheadIndex || 0];\n\n return (\n <Portal origin={origin} isOpen={isOpen} style={this.menuPosition}>\n <ul role=\"menu\" className={styles.typeahead} data-testid=\"typeahead\">\n <FixedSizeList\n ref={this.listRef}\n itemCount={allItems.length}\n itemSize={itemHeight}\n itemKey={(index) => {\n const item = allItems && allItems[index];\n const key = item ? `${index}-${item.label}` : `${index}`;\n return key;\n }}\n width={listWidth}\n height={listHeight}\n >\n {({ index, style }) => {\n const item = allItems && allItems[index];\n if (!item) {\n return null;\n }\n\n return (\n <TypeaheadItem\n onClickItem={() => (this.props.onSelectSuggestion ? this.props.onSelectSuggestion(item) : {})}\n isSelected={typeaheadIndex === null ? false : allItems[typeaheadIndex] === item}\n item={item}\n prefix={prefix}\n style={style}\n onMouseEnter={() => this.onMouseEnter(index)}\n onMouseLeave={this.onMouseLeave}\n />\n );\n }}\n </FixedSizeList>\n </ul>\n\n {showDocumentation && <TypeaheadInfo height={listHeight} item={documentationItem} />}\n </Portal>\n );\n }\n}\n\ninterface PortalProps {\n index?: number;\n isOpen: boolean;\n origin: string;\n style: string;\n}\n\nclass Portal extends PureComponent<React.PropsWithChildren<PortalProps>, {}> {\n node: HTMLElement;\n\n constructor(props: React.PropsWithChildren<PortalProps>) {\n super(props);\n const { index = 0, origin = 'query', style } = props;\n this.node = document.createElement('div');\n this.node.setAttribute('style', style);\n this.node.classList.add(`slate-typeahead-${origin}-${index}`);\n document.body.appendChild(this.node);\n }\n\n componentWillUnmount() {\n document.body.removeChild(this.node);\n }\n\n render() {\n if (this.props.isOpen) {\n this.node.setAttribute('style', this.props.style);\n this.node.classList.add(`slate-typeahead--open`);\n return ReactDOM.createPortal(this.props.children, this.node);\n } else {\n this.node.classList.remove(`slate-typeahead--open`);\n }\n\n return null;\n }\n}\n\nconst getStyles = (theme: GrafanaTheme2) => ({\n typeahead: css({\n position: 'relative',\n zIndex: theme.zIndex.typeahead,\n borderRadius: theme.shape.radius.default,\n border: `1px solid ${theme.components.panel.borderColor}`,\n maxHeight: '66vh',\n overflowY: 'scroll',\n overflowX: 'hidden',\n outline: 'none',\n listStyle: 'none',\n background: theme.components.panel.background,\n color: theme.colors.text.primary,\n boxShadow: theme.shadows.z2,\n\n strong: {\n color: theme.v1.palette.yellow,\n },\n }),\n});\n"],"names":[],"mappings":"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"}