UNPKG

@grafana/ui

Version:
224 lines (221 loc) • 7.66 kB
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