diff --git a/packages/scenes/src/variables/adhoc/AdHocFiltersCombobox/AdHocFiltersComboboxRenderer.tsx b/packages/scenes/src/variables/adhoc/AdHocFiltersCombobox/AdHocFiltersComboboxRenderer.tsx index a5ccf2a1e..66ecaab00 100644 --- a/packages/scenes/src/variables/adhoc/AdHocFiltersCombobox/AdHocFiltersComboboxRenderer.tsx +++ b/packages/scenes/src/variables/adhoc/AdHocFiltersCombobox/AdHocFiltersComboboxRenderer.tsx @@ -1,10 +1,11 @@ import { css, cx } from '@emotion/css'; import { GrafanaTheme2 } from '@grafana/data'; -import { Icon, useStyles2 } from '@grafana/ui'; -import React, { memo, useRef } from 'react'; +import { Icon, Tooltip, useStyles2 } from '@grafana/ui'; +import React, { Fragment, memo, useEffect, useRef, useState } from 'react'; import { AdHocFiltersVariable } from '../AdHocFiltersVariable'; import { AdHocFilterPill } from './AdHocFilterPill'; import { AdHocFiltersAlwaysWipCombobox } from './AdHocFiltersAlwaysWipCombobox'; +import { debounce } from 'lodash'; interface Props { model: AdHocFiltersVariable; @@ -13,6 +14,32 @@ interface Props { export const AdHocFiltersComboboxRenderer = memo(function AdHocFiltersComboboxRenderer({ model }: Props) { const { filters, readOnly } = model.useState(); const styles = useStyles2(getStyles); + const [limitFiltersTo, setLimitFiltersTo] = useState(null); + const wrapperRef = useRef(null); + + const handleCollapseFilters = (shouldCollapse: boolean) => { + if (!shouldCollapse) { + setLimitFiltersTo(null); + return; + } + if (wrapperRef.current) { + const rect = wrapperRef.current.getBoundingClientRect(); + if (rect.height - 6 > 26) { + const componentLineSpan = (rect.height - 6) / 26; + const filterCutOff = Math.max(1, Math.floor(filters.length / (componentLineSpan + 1))); + setLimitFiltersTo(filterCutOff); + } else { + setLimitFiltersTo(null); + } + } + }; + + const debouncedSetActive = debounce(handleCollapseFilters, 100); + + useEffect(() => { + handleCollapseFilters(true); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); // ref that focuses on the always wip filter input // defined in the combobox component via useImperativeHandle @@ -24,10 +51,17 @@ export const AdHocFiltersComboboxRenderer = memo(function AdHocFiltersComboboxRe onClick={() => { focusOnWipInputRef.current?.(); }} + ref={wrapperRef} + onFocusCapture={(e) => { + debouncedSetActive(false); + }} + onBlurCapture={(e) => { + debouncedSetActive(true); + }} > - {filters.map((filter, index) => ( + {(limitFiltersTo ? filters.slice(0, limitFiltersTo) : filters).map((filter, index) => ( ))} + {limitFiltersTo ? ( + + {filters.slice(limitFiltersTo).map((filter, i) => { + const keyLabel = filter.keyLabel ?? filter.key; + // TODO remove when we're on the latest version of @grafana/data + //@ts-expect-error + const valueLabel = filter.valueLabels?.join(', ') || filter.values?.join(', ') || filter.value; + return ( + + {keyLabel} {filter.operator} {valueLabel}
+
+ ); + })} + + } + > +
+{filters.length - limitFiltersTo} filters
+
+ ) : null} + {!readOnly ? : null} ); @@ -72,4 +128,17 @@ const getStyles = (theme: GrafanaTheme2) => ({ color: theme.colors.text.secondary, alignSelf: 'center', }), + basePill: css({ + display: 'flex', + alignItems: 'center', + background: theme.colors.action.selected, + border: `1px solid ${theme.colors.border.weak}`, + padding: theme.spacing(0.125, 1, 0.125, 1), + color: theme.colors.text.primary, + overflow: 'hidden', + whiteSpace: 'nowrap', + minHeight: theme.spacing(2.75), + ...theme.typography.bodySmall, + cursor: 'pointer', + }), });