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 { useStyles2 } from '../../themes';
|
||||||
import { DataLinkBuiltInVars, GrafanaThemeV2, VariableOrigin, VariableSuggestion } from '@grafana/data';
|
import { DataLinkBuiltInVars, GrafanaThemeV2, VariableOrigin, VariableSuggestion } from '@grafana/data';
|
||||||
import { getInputStyles } from '../Input/Input';
|
import { getInputStyles } from '../Input/Input';
|
||||||
|
import CustomScrollbar from '../CustomScrollbar/CustomScrollbar';
|
||||||
|
|
||||||
const modulo = (a: number, n: number) => a - n * Math.floor(a / n);
|
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 });
|
const stateRef = useRef({ showingSuggestions, suggestions, suggestionsIndex, linkUrl, onChange });
|
||||||
stateRef.current = { 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
|
// SelectionReference is used to position the variables suggestion relatively to current DOM selection
|
||||||
const selectionRef = useMemo(() => new SelectionReference(), []);
|
const selectionRef = useMemo(() => new SelectionReference(), []);
|
||||||
|
|
||||||
@ -172,12 +179,15 @@ export const DataLinkInput: React.FC<DataLinkInputProps> = memo(
|
|||||||
{({ ref, style, placement }) => {
|
{({ ref, style, placement }) => {
|
||||||
return (
|
return (
|
||||||
<div ref={ref} style={style} data-placement={placement}>
|
<div ref={ref} style={style} data-placement={placement}>
|
||||||
<DataLinkSuggestions
|
<CustomScrollbar scrollTop={activeIndexPosition} autoHeightMax="300px">
|
||||||
suggestions={stateRef.current.suggestions}
|
<DataLinkSuggestions
|
||||||
onSuggestionSelect={onVariableSelect}
|
activeRef={activeRef}
|
||||||
onClose={() => setShowingSuggestions(false)}
|
suggestions={stateRef.current.suggestions}
|
||||||
activeIndex={suggestionsIndex}
|
onSuggestionSelect={onVariableSelect}
|
||||||
/>
|
onClose={() => setShowingSuggestions(false)}
|
||||||
|
activeIndex={suggestionsIndex}
|
||||||
|
/>
|
||||||
|
</CustomScrollbar>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
@ -208,3 +218,7 @@ export const DataLinkInput: React.FC<DataLinkInputProps> = memo(
|
|||||||
);
|
);
|
||||||
|
|
||||||
DataLinkInput.displayName = 'DataLinkInput';
|
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';
|
import { useStyles2 } from '../../themes';
|
||||||
|
|
||||||
interface DataLinkSuggestionsProps {
|
interface DataLinkSuggestionsProps {
|
||||||
|
activeRef?: React.RefObject<HTMLDivElement>;
|
||||||
suggestions: VariableSuggestion[];
|
suggestions: VariableSuggestion[];
|
||||||
activeIndex: number;
|
activeIndex: number;
|
||||||
onSuggestionSelect: (suggestion: VariableSuggestion) => void;
|
onSuggestionSelect: (suggestion: VariableSuggestion) => void;
|
||||||
@ -23,7 +24,6 @@ const getStyles = (theme: GrafanaThemeV2) => {
|
|||||||
`,
|
`,
|
||||||
wrapper: css`
|
wrapper: css`
|
||||||
background: ${theme.colors.background.primary};
|
background: ${theme.colors.background.primary};
|
||||||
z-index: 1;
|
|
||||||
width: 250px;
|
width: 250px;
|
||||||
box-shadow: 0 5px 10px 0 ${theme.shadows.z1};
|
box-shadow: 0 5px 10px 0 ${theme.shadows.z1};
|
||||||
`,
|
`,
|
||||||
@ -100,10 +100,11 @@ DataLinkSuggestions.displayName = 'DataLinkSuggestions';
|
|||||||
interface DataLinkSuggestionsListProps extends DataLinkSuggestionsProps {
|
interface DataLinkSuggestionsListProps extends DataLinkSuggestionsProps {
|
||||||
label: string;
|
label: string;
|
||||||
activeIndexOffset: number;
|
activeIndexOffset: number;
|
||||||
|
activeRef?: React.RefObject<HTMLDivElement>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DataLinkSuggestionsList: React.FC<DataLinkSuggestionsListProps> = React.memo(
|
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);
|
const styles = useStyles2(getStyles);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -112,9 +113,11 @@ const DataLinkSuggestionsList: React.FC<DataLinkSuggestionsListProps> = React.me
|
|||||||
className={styles.list}
|
className={styles.list}
|
||||||
items={suggestions}
|
items={suggestions}
|
||||||
renderItem={(item, index) => {
|
renderItem={(item, index) => {
|
||||||
|
const isActive = index + activeIndexOffset === activeIndex;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cx(styles.item, index + activeIndexOffset === activeIndex && styles.activeItem)}
|
className={cx(styles.item, isActive && styles.activeItem)}
|
||||||
|
ref={isActive ? selectedRef : undefined}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onSuggestionSelect(item);
|
onSuggestionSelect(item);
|
||||||
}}
|
}}
|
||||||
|
Loading…
Reference in New Issue
Block a user