mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Make sure big lists of suggestions don't expand outside of the viewport (#33520)
* Add CustomScrollbar to data links suggestions * Make sure to scroll to selected suggestion
This commit is contained in:
parent
deb9ead72f
commit
aaca022df6
@ -18,6 +18,7 @@ import { SCHEMA } from '../../utils/slate';
|
||||
import { useStyles2 } from '../../themes';
|
||||
import { DataLinkBuiltInVars, GrafanaThemeV2, VariableOrigin, VariableSuggestion } from '@grafana/data';
|
||||
import { getInputStyles } from '../Input/Input';
|
||||
import CustomScrollbar from '../CustomScrollbar/CustomScrollbar';
|
||||
|
||||
const modulo = (a: number, n: number) => a - n * Math.floor(a / n);
|
||||
|
||||
@ -81,6 +82,12 @@ export const DataLinkInput: React.FC<DataLinkInputProps> = memo(
|
||||
const stateRef = useRef({ showingSuggestions, suggestions, suggestionsIndex, linkUrl, onChange });
|
||||
stateRef.current = { showingSuggestions, suggestions, suggestionsIndex, linkUrl, onChange };
|
||||
|
||||
// Used to get the height of the suggestion elements in order to scroll to them.
|
||||
const activeRef = useRef<HTMLDivElement>(null);
|
||||
const activeIndexPosition = useMemo(() => getElementPosition(activeRef.current, suggestionsIndex), [
|
||||
suggestionsIndex,
|
||||
]);
|
||||
|
||||
// SelectionReference is used to position the variables suggestion relatively to current DOM selection
|
||||
const selectionRef = useMemo(() => new SelectionReference(), []);
|
||||
|
||||
@ -172,12 +179,15 @@ export const DataLinkInput: React.FC<DataLinkInputProps> = memo(
|
||||
{({ ref, style, placement }) => {
|
||||
return (
|
||||
<div ref={ref} style={style} data-placement={placement}>
|
||||
<DataLinkSuggestions
|
||||
suggestions={stateRef.current.suggestions}
|
||||
onSuggestionSelect={onVariableSelect}
|
||||
onClose={() => setShowingSuggestions(false)}
|
||||
activeIndex={suggestionsIndex}
|
||||
/>
|
||||
<CustomScrollbar scrollTop={activeIndexPosition} autoHeightMax="300px">
|
||||
<DataLinkSuggestions
|
||||
activeRef={activeRef}
|
||||
suggestions={stateRef.current.suggestions}
|
||||
onSuggestionSelect={onVariableSelect}
|
||||
onClose={() => setShowingSuggestions(false)}
|
||||
activeIndex={suggestionsIndex}
|
||||
/>
|
||||
</CustomScrollbar>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
@ -208,3 +218,7 @@ export const DataLinkInput: React.FC<DataLinkInputProps> = memo(
|
||||
);
|
||||
|
||||
DataLinkInput.displayName = 'DataLinkInput';
|
||||
|
||||
function getElementPosition(suggestionElement: HTMLElement | null, activeIndex: number) {
|
||||
return (suggestionElement?.clientHeight ?? 0) * activeIndex;
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import { List } from '../index';
|
||||
import { useStyles2 } from '../../themes';
|
||||
|
||||
interface DataLinkSuggestionsProps {
|
||||
activeRef?: React.RefObject<HTMLDivElement>;
|
||||
suggestions: VariableSuggestion[];
|
||||
activeIndex: number;
|
||||
onSuggestionSelect: (suggestion: VariableSuggestion) => void;
|
||||
@ -23,7 +24,6 @@ const getStyles = (theme: GrafanaThemeV2) => {
|
||||
`,
|
||||
wrapper: css`
|
||||
background: ${theme.colors.background.primary};
|
||||
z-index: 1;
|
||||
width: 250px;
|
||||
box-shadow: 0 5px 10px 0 ${theme.shadows.z1};
|
||||
`,
|
||||
@ -100,10 +100,11 @@ DataLinkSuggestions.displayName = 'DataLinkSuggestions';
|
||||
interface DataLinkSuggestionsListProps extends DataLinkSuggestionsProps {
|
||||
label: string;
|
||||
activeIndexOffset: number;
|
||||
activeRef?: React.RefObject<HTMLDivElement>;
|
||||
}
|
||||
|
||||
const DataLinkSuggestionsList: React.FC<DataLinkSuggestionsListProps> = React.memo(
|
||||
({ activeIndex, activeIndexOffset, label, onClose, onSuggestionSelect, suggestions }) => {
|
||||
({ activeIndex, activeIndexOffset, label, onClose, onSuggestionSelect, suggestions, activeRef: selectedRef }) => {
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
return (
|
||||
@ -112,9 +113,11 @@ const DataLinkSuggestionsList: React.FC<DataLinkSuggestionsListProps> = React.me
|
||||
className={styles.list}
|
||||
items={suggestions}
|
||||
renderItem={(item, index) => {
|
||||
const isActive = index + activeIndexOffset === activeIndex;
|
||||
return (
|
||||
<div
|
||||
className={cx(styles.item, index + activeIndexOffset === activeIndex && styles.activeItem)}
|
||||
className={cx(styles.item, isActive && styles.activeItem)}
|
||||
ref={isActive ? selectedRef : undefined}
|
||||
onClick={() => {
|
||||
onSuggestionSelect(item);
|
||||
}}
|
||||
|
Loading…
Reference in New Issue
Block a user