mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge remote-tracking branch 'origin/main' into resource-store
This commit is contained in:
commit
6a08224725
@ -1,129 +0,0 @@
|
|||||||
// Core Grafana history https://github.com/grafana/grafana/blob/v11.0.0-preview/packages/grafana-ui/src/components/Select/SelectBase.tsx
|
|
||||||
import { cx } from '@emotion/css';
|
|
||||||
import { max } from 'lodash';
|
|
||||||
import { RefCallback } from 'react';
|
|
||||||
import * as React from 'react';
|
|
||||||
import { MenuListProps } from 'react-select';
|
|
||||||
import { FixedSizeList as List } from 'react-window';
|
|
||||||
|
|
||||||
import { SelectableValue, toIconName } from '@grafana/data';
|
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
|
||||||
import { CustomScrollbar, Icon, getSelectStyles, useTheme2 } from '@grafana/ui';
|
|
||||||
|
|
||||||
interface SelectMenuProps {
|
|
||||||
maxHeight: number;
|
|
||||||
innerRef: RefCallback<HTMLDivElement>;
|
|
||||||
innerProps: {};
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SelectMenu = ({ children, maxHeight, innerRef, innerProps }: React.PropsWithChildren<SelectMenuProps>) => {
|
|
||||||
const theme = useTheme2();
|
|
||||||
const styles = getSelectStyles(theme);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div {...innerProps} className={styles.menu} style={{ maxHeight }} aria-label="Select options menu">
|
|
||||||
<CustomScrollbar scrollRefCallback={innerRef} autoHide={false} autoHeightMax="inherit" hideHorizontalTrack>
|
|
||||||
{children}
|
|
||||||
</CustomScrollbar>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
SelectMenu.displayName = 'SelectMenu';
|
|
||||||
|
|
||||||
const VIRTUAL_LIST_ITEM_HEIGHT = 37;
|
|
||||||
const VIRTUAL_LIST_WIDTH_ESTIMATE_MULTIPLIER = 7;
|
|
||||||
|
|
||||||
// A virtualized version of the SelectMenu, descriptions for SelectableValue options not supported since those are of a variable height.
|
|
||||||
//
|
|
||||||
// To support the virtualized list we have to "guess" the width of the menu container based on the longest available option.
|
|
||||||
// the reason for this is because all of the options will be positioned absolute, this takes them out of the document and no space
|
|
||||||
// is created for them, thus the container can't grow to accomodate.
|
|
||||||
//
|
|
||||||
// VIRTUAL_LIST_ITEM_HEIGHT and WIDTH_ESTIMATE_MULTIPLIER are both magic numbers.
|
|
||||||
// Some characters (such as emojis and other unicode characters) may consist of multiple code points in which case the width would be inaccurate (but larger than needed).
|
|
||||||
export const VirtualizedSelectMenu = ({ children, maxHeight, options, getValue }: MenuListProps<SelectableValue>) => {
|
|
||||||
const theme = useTheme2();
|
|
||||||
const styles = getSelectStyles(theme);
|
|
||||||
const [value] = getValue();
|
|
||||||
|
|
||||||
const valueIndex = value ? options.findIndex((option: SelectableValue<unknown>) => option.value === value.value) : 0;
|
|
||||||
const initialOffset = valueIndex * VIRTUAL_LIST_ITEM_HEIGHT;
|
|
||||||
|
|
||||||
if (!Array.isArray(children)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const longestOption = max(options.map((option) => option.label?.length)) ?? 0;
|
|
||||||
const widthEstimate = longestOption * VIRTUAL_LIST_WIDTH_ESTIMATE_MULTIPLIER;
|
|
||||||
const heightEstimate = Math.min(options.length * VIRTUAL_LIST_ITEM_HEIGHT, maxHeight);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<List
|
|
||||||
className={styles.menu}
|
|
||||||
height={heightEstimate}
|
|
||||||
width={widthEstimate}
|
|
||||||
aria-label="Select options menu"
|
|
||||||
itemCount={children.length}
|
|
||||||
itemSize={VIRTUAL_LIST_ITEM_HEIGHT}
|
|
||||||
initialScrollOffset={initialOffset}
|
|
||||||
>
|
|
||||||
{({ index, style }) => <div style={{ ...style, overflow: 'hidden' }}>{children[index]}</div>}
|
|
||||||
</List>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
VirtualizedSelectMenu.displayName = 'VirtualizedSelectMenu';
|
|
||||||
|
|
||||||
interface SelectMenuOptionProps<T> {
|
|
||||||
isDisabled: boolean;
|
|
||||||
isFocused: boolean;
|
|
||||||
isSelected: boolean;
|
|
||||||
innerProps: JSX.IntrinsicElements['div'];
|
|
||||||
innerRef: RefCallback<HTMLDivElement>;
|
|
||||||
renderOptionLabel?: (value: SelectableValue<T>) => JSX.Element;
|
|
||||||
data: SelectableValue<T>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const SelectMenuOptions = ({
|
|
||||||
children,
|
|
||||||
data,
|
|
||||||
innerProps,
|
|
||||||
innerRef,
|
|
||||||
isFocused,
|
|
||||||
isSelected,
|
|
||||||
renderOptionLabel,
|
|
||||||
}: React.PropsWithChildren<SelectMenuOptionProps<unknown>>) => {
|
|
||||||
const theme = useTheme2();
|
|
||||||
const styles = getSelectStyles(theme);
|
|
||||||
const icon = data.icon ? toIconName(data.icon) : undefined;
|
|
||||||
// We are removing onMouseMove and onMouseOver from innerProps because they cause the whole
|
|
||||||
// list to re-render everytime the user hovers over an option. This is a performance issue.
|
|
||||||
// See https://github.com/JedWatson/react-select/issues/3128#issuecomment-451936743
|
|
||||||
const { onMouseMove, onMouseOver, ...rest } = innerProps;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
ref={innerRef}
|
|
||||||
className={cx(
|
|
||||||
styles.option,
|
|
||||||
isFocused && styles.optionFocused,
|
|
||||||
isSelected && styles.optionSelected,
|
|
||||||
data.isDisabled && styles.optionDisabled
|
|
||||||
)}
|
|
||||||
{...rest}
|
|
||||||
data-testid={selectors.components.Select.option}
|
|
||||||
title={data.title}
|
|
||||||
>
|
|
||||||
{icon && <Icon name={icon} className={styles.optionIcon} />}
|
|
||||||
{data.imgUrl && <img className={styles.optionImage} src={data.imgUrl} alt={data.label || String(data.value)} />}
|
|
||||||
<div className={styles.optionBody}>
|
|
||||||
<span>{renderOptionLabel ? renderOptionLabel(data) : children}</span>
|
|
||||||
{data.description && <div className={styles.optionDescription}>{data.description}</div>}
|
|
||||||
{data.component && <data.component />}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
SelectMenuOptions.displayName = 'SelectMenuOptions';
|
|
@ -18,12 +18,12 @@ import {
|
|||||||
Icon,
|
Icon,
|
||||||
InlineField,
|
InlineField,
|
||||||
InlineFieldRow,
|
InlineFieldRow,
|
||||||
|
SelectMenuOptions,
|
||||||
useStyles2,
|
useStyles2,
|
||||||
useTheme2,
|
useTheme2,
|
||||||
} from '@grafana/ui';
|
} from '@grafana/ui';
|
||||||
|
|
||||||
import { PrometheusDatasource } from '../../datasource';
|
import { PrometheusDatasource } from '../../datasource';
|
||||||
import { SelectMenuOptions } from '../../gcopypaste/packages/grafana-ui/src/components/Select/SelectBase';
|
|
||||||
import { truncateResult } from '../../language_utils';
|
import { truncateResult } from '../../language_utils';
|
||||||
import { regexifyLabelValuesQueryString } from '../parsingUtils';
|
import { regexifyLabelValuesQueryString } from '../parsingUtils';
|
||||||
import { QueryBuilderLabelFilter } from '../shared/types';
|
import { QueryBuilderLabelFilter } from '../shared/types';
|
||||||
|
Loading…
Reference in New Issue
Block a user