@grafana/ui
Version:
Grafana Components Library
224 lines (221 loc) • 7.66 kB
JavaScript
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
import { css, cx } from '@emotion/css';
import { max } from 'lodash';
import * as React from 'react';
import { useRef, useMemo, useLayoutEffect } from 'react';
import { FixedSizeList } from 'react-window';
import { toIconName } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { t, Trans } from '@grafana/i18n';
import { useTheme2 } from '../../themes/ThemeContext.mjs';
import { clearButtonStyles } from '../Button/Button.mjs';
import { Icon } from '../Icon/Icon.mjs';
import { ScrollContainer } from '../ScrollContainer/ScrollContainer.mjs';
import { getSelectStyles } from './getSelectStyles.mjs';
import { ToggleAllState } from './types.mjs';
"use strict";
const SelectMenu = ({
children,
maxHeight,
innerRef,
innerProps,
selectProps
}) => {
var _a;
const theme = useTheme2();
const styles = getSelectStyles(theme);
const { toggleAllOptions, components } = selectProps;
const optionsElement = (_a = components == null ? void 0 : components.Option) != null ? _a : SelectMenuOptions;
return /* @__PURE__ */ jsx(
"div",
{
...innerProps,
"data-testid": selectors.components.Select.menu,
className: styles.menu,
style: { maxHeight },
"aria-label": t("grafana-ui.select.menu-label", "Select options menu"),
children: /* @__PURE__ */ jsxs(ScrollContainer, { ref: innerRef, maxHeight: "inherit", overflowX: "hidden", showScrollIndicators: true, padding: 0.5, children: [
toggleAllOptions && /* @__PURE__ */ jsx(
ToggleAllOption,
{
state: toggleAllOptions.state,
optionComponent: optionsElement,
selectedCount: toggleAllOptions.selectedCount,
onClick: toggleAllOptions.selectAllClicked
}
),
children
] })
}
);
};
SelectMenu.displayName = "SelectMenu";
const VIRTUAL_LIST_ITEM_HEIGHT = 37;
const VIRTUAL_LIST_WIDTH_ESTIMATE_MULTIPLIER = 8;
const VIRTUAL_LIST_PADDING = 8;
const VIRTUAL_LIST_WIDTH_EXTRA = 58;
const VirtualizedSelectMenu = ({
children,
maxHeight,
innerRef: scrollRef,
options,
selectProps,
focusedOption
}) => {
var _a, _b;
const theme = useTheme2();
const styles = getSelectStyles(theme);
const listRef = useRef(null);
const { toggleAllOptions, components } = selectProps;
const optionComponent = (_a = components == null ? void 0 : components.Option) != null ? _a : SelectMenuOptions;
const flattenedOptions = useMemo(
() => options.flatMap((option) => option.options ? [option, ...option.options] : [option]),
[options]
);
const focusedIndex = flattenedOptions.findIndex(
(option) => option.value === (focusedOption == null ? void 0 : focusedOption.value)
);
useLayoutEffect(() => {
var _a2;
(_a2 = listRef.current) == null ? void 0 : _a2.scrollToItem(focusedIndex);
}, [focusedIndex]);
if (!Array.isArray(children)) {
return null;
}
const flattenedChildren = children.flatMap((child, index) => {
if (hasArrayChildren(child)) {
const childWithoutChildren = React.cloneElement(child, {
children: null
});
return [
childWithoutChildren,
...child.props.children.slice(0, -1),
// add a bottom divider to the last item in the category
React.cloneElement(child.props.children.at(-1), {
innerProps: {
...child.props.children.at(-1).props.innerProps,
style: {
borderBottom: `1px solid ${theme.colors.border.weak}`,
height: VIRTUAL_LIST_ITEM_HEIGHT
}
}
})
];
}
return [child];
});
if (toggleAllOptions) {
flattenedChildren.unshift(
/* @__PURE__ */ jsx(
ToggleAllOption,
{
optionComponent,
state: toggleAllOptions.state,
selectedCount: toggleAllOptions.selectedCount,
onClick: toggleAllOptions.selectAllClicked
}
)
);
}
let longestOption = (_b = max(flattenedOptions.map((option) => {
var _a2;
return (_a2 = option.label) == null ? void 0 : _a2.length;
}))) != null ? _b : 0;
if (toggleAllOptions && longestOption < 12) {
longestOption = 12;
}
const widthEstimate = longestOption * VIRTUAL_LIST_WIDTH_ESTIMATE_MULTIPLIER + VIRTUAL_LIST_PADDING * 2 + VIRTUAL_LIST_WIDTH_EXTRA;
const heightEstimate = Math.min(flattenedChildren.length * VIRTUAL_LIST_ITEM_HEIGHT, maxHeight);
return /* @__PURE__ */ jsx(
FixedSizeList,
{
outerRef: scrollRef,
ref: listRef,
className: styles.menu,
height: heightEstimate,
width: widthEstimate,
"aria-label": t("grafana-ui.select.menu-label", "Select options menu"),
itemCount: flattenedChildren.length,
itemSize: VIRTUAL_LIST_ITEM_HEIGHT,
children: ({ index, style }) => /* @__PURE__ */ jsx("div", { style: { ...style, overflow: "hidden" }, children: flattenedChildren[index] })
}
);
};
const hasArrayChildren = (child) => {
return React.isValidElement(child) && Array.isArray(child.props.children);
};
VirtualizedSelectMenu.displayName = "VirtualizedSelectMenu";
const ToggleAllOption = ({
state,
onClick,
selectedCount,
optionComponent
}) => {
const theme = useTheme2();
const styles = getSelectStyles(theme);
return /* @__PURE__ */ jsx(
"button",
{
"data-testid": selectors.components.Select.toggleAllOptions,
className: css(clearButtonStyles(theme), styles.toggleAllButton, {
height: VIRTUAL_LIST_ITEM_HEIGHT
}),
onClick,
children: optionComponent({
isDisabled: false,
isSelected: state === ToggleAllState.allSelected,
isFocused: false,
data: {},
indeterminate: state === ToggleAllState.indeterminate,
innerRef: () => {
},
innerProps: {},
children: /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(Trans, { i18nKey: "select.select-menu.selected-count", children: "Selected" }),
` (${selectedCount != null ? selectedCount : 0})`
] })
})
}
);
};
const SelectMenuOptions = ({
children,
data,
innerProps,
innerRef,
isFocused,
isSelected,
renderOptionLabel
}) => {
const theme = useTheme2();
const styles = getSelectStyles(theme);
const icon = data.icon ? toIconName(data.icon) : void 0;
const { onMouseMove, onMouseOver, ...rest } = innerProps;
return /* @__PURE__ */ jsxs(
"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,
children: [
icon && /* @__PURE__ */ jsx(Icon, { name: icon, className: styles.optionIcon }),
data.imgUrl && /* @__PURE__ */ jsx("img", { className: styles.optionImage, src: data.imgUrl, alt: data.label || String(data.value) }),
/* @__PURE__ */ jsxs("div", { className: styles.optionBody, children: [
/* @__PURE__ */ jsx("span", { children: renderOptionLabel ? renderOptionLabel(data) : children }),
data.description && /* @__PURE__ */ jsx("div", { className: styles.optionDescription, children: data.description }),
data.component && /* @__PURE__ */ jsx(data.component, {})
] })
]
}
);
};
SelectMenuOptions.displayName = "SelectMenuOptions";
export { SelectMenu, SelectMenuOptions, VirtualizedSelectMenu };
//# sourceMappingURL=SelectMenu.mjs.map