@grafana/ui
Version:
Grafana Components Library
101 lines (98 loc) • 4.28 kB
JavaScript
import { autoPlacement, size, useFloating, autoUpdate } from '@floating-ui/react';
import { useRef, useState, useMemo } from 'react';
import { t } from '@grafana/i18n';
import { BOUNDARY_ELEMENT_ID } from '../../utils/floating.mjs';
import { measureText } from '../../utils/measureText.mjs';
import { NO_OPTIONS_I18N_KEY } from './MessageRows.mjs';
import { MENU_OPTION_HEIGHT, POPOVER_MAX_HEIGHT, MENU_ITEM_FONT_SIZE, MENU_ITEM_FONT_WEIGHT, MENU_ITEM_DESCRIPTION_FONT_SIZE, MENU_ITEM_PADDING } from './getComboboxStyles.mjs';
;
const WIDTH_CALCULATION_LIMIT_ITEMS = 1e5;
const POPOVER_PADDING = 16;
const SCROLL_CONTAINER_PADDING = 8;
const ICON_WIDTH = 28;
const MESSAGE_ROW_PADDING = 32;
const useComboboxFloat = (items, isOpen) => {
var _a, _b;
const inputRef = useRef(null);
const floatingRef = useRef(null);
const scrollRef = useRef(null);
const [popoverMaxSize, setPopoverMaxSize] = useState({
width: 0,
height: 0
});
const scrollbarWidth = useMemo(() => getScrollbarWidth(), []);
const middleware = [
autoPlacement({
// see https://floating-ui.com/docs/autoplacement
allowedPlacements: ["bottom-start", "bottom-end", "top-start", "top-end"],
boundary: (_a = document.getElementById(BOUNDARY_ELEMENT_ID)) != null ? _a : void 0,
crossAxis: true
}),
size({
apply({ availableWidth, availableHeight }) {
const preferredMaxWidth = availableWidth - POPOVER_PADDING;
const preferredMaxHeight = availableHeight - POPOVER_PADDING;
const width = Math.max(preferredMaxWidth, 0);
const height = Math.min(Math.max(preferredMaxHeight, MENU_OPTION_HEIGHT * 6), POPOVER_MAX_HEIGHT);
setPopoverMaxSize({ width, height });
}
})
];
const elements = { reference: inputRef.current, floating: floatingRef.current };
const { floatingStyles } = useFloating({
strategy: "fixed",
open: isOpen,
placement: "bottom-start",
middleware,
elements,
whileElementsMounted: autoUpdate
});
const longestItemWidth = useMemo(() => {
var _a2, _b2;
let longestLabel = "";
let longestLabelIndex = -1;
let longestDescription = "";
const itemsToLookAt = Math.min(items.length, WIDTH_CALCULATION_LIMIT_ITEMS);
for (let i = 0; i < itemsToLookAt; i++) {
const itemLabel = (_a2 = items[i].label) != null ? _a2 : items[i].value.toString();
if (itemLabel.length > longestLabel.length) {
longestLabel = itemLabel;
longestLabelIndex = i;
}
const itemDescription = (_b2 = items[i].description) != null ? _b2 : "";
if (itemDescription.length > longestDescription.length) {
longestDescription = itemDescription;
}
}
const labelWidth = measureText(longestLabel, MENU_ITEM_FONT_SIZE, MENU_ITEM_FONT_WEIGHT).width;
const descriptionWidth = longestDescription ? measureText(longestDescription, MENU_ITEM_DESCRIPTION_FONT_SIZE).width : 0;
const iconSize = longestLabelIndex > -1 && items[longestLabelIndex].icon ? ICON_WIDTH : 0;
const textWidth = Math.max(labelWidth + iconSize, descriptionWidth);
const itemWidth = textWidth + SCROLL_CONTAINER_PADDING + MENU_ITEM_PADDING * 2 + scrollbarWidth;
const noOptionsText = t(NO_OPTIONS_I18N_KEY, "No options found.");
const noOptionsWidth = measureText(noOptionsText, MENU_ITEM_FONT_SIZE).width + MESSAGE_ROW_PADDING + scrollbarWidth;
return Math.max(itemWidth, noOptionsWidth);
}, [items, scrollbarWidth]);
const floatStyles = {
...floatingStyles,
width: longestItemWidth,
maxWidth: popoverMaxSize.width,
minWidth: (_b = inputRef.current) == null ? void 0 : _b.offsetWidth,
maxHeight: popoverMaxSize.height
};
return { inputRef, floatingRef, scrollRef, floatStyles };
};
function getScrollbarWidth() {
var _a;
const outer = document.createElement("div");
outer.style.visibility = "hidden";
outer.style.overflow = "scroll";
document.body.appendChild(outer);
const inner = document.createElement("div");
outer.appendChild(inner);
const scrollbarWidth = outer.offsetWidth - inner.offsetWidth;
(_a = outer.parentNode) == null ? void 0 : _a.removeChild(outer);
return scrollbarWidth;
}
export { useComboboxFloat };
//# sourceMappingURL=useComboboxFloat.mjs.map