@grafana/scenes 7.3.0 → 7.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7820,806 +7820,931 @@ const getStyles$d = (theme) => ({
7820
7820
  })
7821
7821
  });
7822
7822
 
7823
- const MAX_VISIBLE_FILTERS_DEFAULT = 4;
7824
- const MAX_VISIBLE_FILTERS_WITH_GROUP_BY = 2;
7825
- const MAX_VISIBLE_GROUP_BY = 2;
7826
- const AdHocFiltersComboboxRenderer = React.memo(function AdHocFiltersComboboxRenderer2({ controller }) {
7827
- var _a;
7828
- const { originFilters, filters, readOnly, collapsible, valueRecommendations, enableGroupBy, groupByRestorable } = controller.useState();
7823
+ function DrilldownRecommendations({ recentDrilldowns, recommendedDrilldowns, showRecommended = false }) {
7829
7824
  const styles = ui.useStyles2(getStyles$c);
7830
- const theme = ui.useTheme2();
7831
- const [collapsed, setCollapsed] = React.useState(true);
7832
- const [wrapperRef, { height: wrapperHeight }] = reactUse.useMeasure();
7833
- const clearAll = () => {
7834
- var _a2;
7835
- (_a2 = controller.clearAll) == null ? void 0 : _a2.call(controller);
7836
- };
7837
- const focusOnWipInputRef = React.useRef();
7838
- const focusOnGroupByWipInputRef = React.useRef();
7839
- const singleLineThreshold = theme.spacing.gridSize * 5;
7840
- const isMultiLine = collapsible && wrapperHeight > singleLineThreshold;
7841
- const handleCollapseToggle = (event) => {
7842
- event.stopPropagation();
7843
- if (collapsible) {
7844
- setCollapsed(true);
7845
- }
7825
+ const [isPopoverVisible, setPopoverVisible] = React.useState(false);
7826
+ const ref = React.useRef(null);
7827
+ const openPopover = () => {
7828
+ setPopoverVisible(true);
7846
7829
  };
7847
- const handleExpand = () => {
7848
- if (collapsible && collapsed) {
7849
- setCollapsed(false);
7850
- }
7830
+ const onClickAction = (action) => {
7831
+ action();
7832
+ setPopoverVisible(false);
7851
7833
  };
7852
- const visibleOriginFilters = (_a = originFilters == null ? void 0 : originFilters.filter((f) => f.origin && !f.hidden && !f.dismissedGroupBy)) != null ? _a : [];
7853
- const visibleFilters = filters.filter((f) => !f.hidden);
7854
- const allFilters = [...visibleOriginFilters, ...visibleFilters];
7855
- const totalFiltersCount = allFilters.length;
7856
- const adhocFilters = allFilters.filter((f) => !isGroupByFilter(f));
7857
- const groupByFilters = allFilters.filter(isGroupByFilter);
7858
- const shouldCollapse = collapsible && collapsed && totalFiltersCount > 0;
7859
- const maxVisibleAdhocFilters = enableGroupBy ? MAX_VISIBLE_FILTERS_WITH_GROUP_BY : MAX_VISIBLE_FILTERS_DEFAULT;
7860
- const adhocFiltersToRender = shouldCollapse ? adhocFilters.slice(0, maxVisibleAdhocFilters) : adhocFilters;
7861
- const adhocHiddenCount = shouldCollapse ? Math.max(0, adhocFilters.length - maxVisibleAdhocFilters) : 0;
7862
- const groupByFiltersToRender = shouldCollapse ? groupByFilters.slice(0, MAX_VISIBLE_GROUP_BY) : groupByFilters;
7863
- const groupByHiddenCount = shouldCollapse ? Math.max(0, groupByFilters.length - MAX_VISIBLE_GROUP_BY) : 0;
7864
- React.useEffect(() => {
7865
- if (collapsible && totalFiltersCount === 0 && collapsed) {
7866
- setCollapsed(false);
7867
- }
7868
- }, [collapsible, totalFiltersCount, collapsed]);
7869
- const showCollapseButton = collapsible && isMultiLine && !collapsed;
7870
- const showExpandButton = shouldCollapse && (adhocHiddenCount > 0 || groupByHiddenCount > 0);
7871
- return /* @__PURE__ */ React__default.default.createElement(
7834
+ const content = /* @__PURE__ */ React__default.default.createElement(ui.ClickOutsideWrapper, { onClick: () => setPopoverVisible(false), useCapture: true }, /* @__PURE__ */ React__default.default.createElement("div", { className: styles.menuContainer, onClick: (ev) => ev.stopPropagation() }, /* @__PURE__ */ React__default.default.createElement(ui.Stack, { direction: "column" }, /* @__PURE__ */ React__default.default.createElement(ui.Text, { weight: "bold", variant: "bodySmall", color: "secondary" }, /* @__PURE__ */ React__default.default.createElement(i18n.Trans, { i18nKey: "grafana-scenes.components.drilldown-recommendations.recent" }, "Recent")), recentDrilldowns && recentDrilldowns.length > 0 ? recentDrilldowns.map((drilldown) => /* @__PURE__ */ React__default.default.createElement(
7872
7835
  "div",
7873
7836
  {
7874
- ref: wrapperRef,
7875
- className: css.cx(styles.comboboxWrapper, {
7876
- [styles.comboboxFocusOutline]: !readOnly,
7877
- [styles.collapsed]: shouldCollapse
7878
- })
7837
+ key: drilldown.label,
7838
+ className: css.cx(styles.combinedFilterPill),
7839
+ onClick: () => onClickAction(drilldown.onClick)
7879
7840
  },
7880
- !readOnly && valueRecommendations && /* @__PURE__ */ React__default.default.createElement(valueRecommendations.Component, { model: valueRecommendations }),
7881
- adhocFiltersToRender.map((filter, index) => /* @__PURE__ */ React__default.default.createElement(
7882
- AdHocFilterPill,
7883
- {
7884
- key: `${filter.origin ? "origin-" : ""}${index}-${filter.key}`,
7885
- filter,
7886
- controller,
7887
- readOnly: readOnly || filter.readOnly,
7888
- focusOnWipInputRef: focusOnWipInputRef.current
7889
- }
7890
- )),
7891
- shouldCollapse && adhocHiddenCount > 0 && /* @__PURE__ */ React__default.default.createElement(
7892
- ui.Button,
7893
- {
7894
- className: styles.moreIndicator,
7895
- fill: "text",
7896
- size: "sm",
7897
- "aria-label": i18n.t(
7898
- "grafana-scenes.variables.adhoc-filters-combobox-renderer.show-more-filters",
7899
- "Show {{count}} more filters",
7900
- { count: adhocHiddenCount }
7901
- ),
7902
- onClick: (e) => {
7903
- e.stopPropagation();
7904
- handleExpand();
7905
- setTimeout(() => {
7906
- var _a2;
7907
- return (_a2 = focusOnWipInputRef.current) == null ? void 0 : _a2.call(focusOnWipInputRef);
7908
- });
7909
- }
7910
- },
7911
- "+",
7912
- adhocHiddenCount
7913
- ),
7914
- !readOnly && /* @__PURE__ */ React__default.default.createElement(AdHocFiltersAlwaysWipCombobox, { ref: focusOnWipInputRef, controller, onInputClick: handleExpand }),
7915
- enableGroupBy && /* @__PURE__ */ React__default.default.createElement(React__default.default.Fragment, null, /* @__PURE__ */ React__default.default.createElement("div", { className: styles.sectionDivider }), /* @__PURE__ */ React__default.default.createElement("span", { className: styles.groupByLabel }, i18n.t("grafana-scenes.variables.adhoc-filters-combobox-renderer.group-by-label", "Group by:")), groupByFiltersToRender.map((filter, index) => /* @__PURE__ */ React__default.default.createElement(
7916
- GroupByPill,
7917
- {
7918
- key: `groupby-${index}-${filter.key}`,
7919
- filter,
7920
- controller,
7921
- readOnly: readOnly || filter.readOnly,
7922
- focusOnWipInputRef: focusOnGroupByWipInputRef.current
7923
- }
7924
- )), shouldCollapse && groupByHiddenCount > 0 && /* @__PURE__ */ React__default.default.createElement(
7925
- ui.Button,
7926
- {
7927
- className: styles.moreIndicator,
7928
- fill: "text",
7929
- size: "sm",
7930
- "aria-label": i18n.t(
7931
- "grafana-scenes.variables.adhoc-filters-combobox-renderer.show-more-group-by",
7932
- "Show {{count}} more group by",
7933
- { count: groupByHiddenCount }
7934
- ),
7935
- onClick: (e) => {
7936
- e.stopPropagation();
7937
- handleExpand();
7938
- setTimeout(() => {
7939
- var _a2;
7940
- return (_a2 = focusOnGroupByWipInputRef.current) == null ? void 0 : _a2.call(focusOnGroupByWipInputRef);
7941
- });
7942
- }
7943
- },
7944
- "+",
7945
- groupByHiddenCount
7946
- ), !readOnly && /* @__PURE__ */ React__default.default.createElement(
7947
- AdHocFiltersAlwaysWipCombobox,
7948
- {
7949
- ref: focusOnGroupByWipInputRef,
7950
- controller,
7951
- onInputClick: handleExpand,
7952
- isGroupBy: true
7841
+ drilldown.label
7842
+ )) : /* @__PURE__ */ React__default.default.createElement("div", { className: styles.emptyMessage }, /* @__PURE__ */ React__default.default.createElement(i18n.Trans, { i18nKey: "grafana-scenes.components.drilldown-recommendations.recent-empty" }, "No recent values")), showRecommended && /* @__PURE__ */ React__default.default.createElement(React__default.default.Fragment, null, /* @__PURE__ */ React__default.default.createElement(ui.Text, { weight: "bold", variant: "bodySmall", color: "secondary" }, /* @__PURE__ */ React__default.default.createElement(i18n.Trans, { i18nKey: "grafana-scenes.components.drilldown-recommendations.recommended" }, "Recommended")), recommendedDrilldowns && recommendedDrilldowns.length > 0 ? recommendedDrilldowns.map((drilldown) => /* @__PURE__ */ React__default.default.createElement(
7843
+ "div",
7844
+ {
7845
+ key: drilldown.label,
7846
+ className: css.cx(styles.combinedFilterPill),
7847
+ onClick: () => onClickAction(drilldown.onClick)
7848
+ },
7849
+ drilldown.label
7850
+ )) : /* @__PURE__ */ React__default.default.createElement("div", { className: styles.emptyMessage }, /* @__PURE__ */ React__default.default.createElement(i18n.Trans, { i18nKey: "grafana-scenes.components.drilldown-recommendations.recommended-empty" }, "No recommended values"))))));
7851
+ return /* @__PURE__ */ React__default.default.createElement(React__default.default.Fragment, null, /* @__PURE__ */ React__default.default.createElement(
7852
+ ui.IconButton,
7853
+ {
7854
+ name: "bars-clock",
7855
+ tooltip: i18n.t("grafana-scenes.components.drilldown-recommendations.tooltip", "Show recommendations"),
7856
+ ref,
7857
+ className: css.cx(isPopoverVisible && styles.iconActive),
7858
+ onClick: (ev) => {
7859
+ openPopover();
7860
+ ev.stopPropagation();
7953
7861
  }
7954
- ), groupByRestorable && /* @__PURE__ */ React__default.default.createElement(
7955
- ui.IconButton,
7956
- {
7957
- name: "history",
7958
- size: "md",
7959
- className: styles.controlButton,
7960
- tooltip: i18n.t(
7961
- "grafana-scenes.variables.adhoc-filters-combobox-renderer.restore-default-group-by",
7962
- "Restore groupby set by this dashboard."
7963
- ),
7964
- onClick: () => {
7965
- var _a2;
7966
- return (_a2 = controller.restoreOriginalGroupBy) == null ? void 0 : _a2.call(controller);
7862
+ }
7863
+ ), isPopoverVisible && ref.current && /* @__PURE__ */ React__default.default.createElement(
7864
+ ui.Popover,
7865
+ {
7866
+ content,
7867
+ onKeyDown: (event) => {
7868
+ if (event.key === " ") {
7869
+ event.stopPropagation();
7967
7870
  }
7968
- }
7969
- )),
7970
- (showCollapseButton || showExpandButton || !readOnly) && /* @__PURE__ */ React__default.default.createElement("div", { className: styles.rightControls }, /* @__PURE__ */ React__default.default.createElement("div", { className: styles.sectionDivider }), showCollapseButton && /* @__PURE__ */ React__default.default.createElement(
7971
- ui.Button,
7972
- {
7973
- className: styles.collapseButton,
7974
- fill: "text",
7975
- size: "sm",
7976
- onClick: handleCollapseToggle,
7977
- "aria-label": i18n.t(
7978
- "grafana-scenes.variables.adhoc-filters-combobox-renderer.collapse-filters",
7979
- "Collapse filters"
7980
- ),
7981
- "aria-expanded": !collapsed
7982
7871
  },
7983
- i18n.t("grafana-scenes.variables.adhoc-filters-combobox-renderer.collapse", "Collapse"),
7984
- /* @__PURE__ */ React__default.default.createElement(ui.Icon, { name: "angle-up", size: "md" })
7985
- ), showExpandButton && /* @__PURE__ */ React__default.default.createElement(
7986
- ui.IconButton,
7987
- {
7988
- name: "angle-down",
7989
- size: "md",
7990
- className: styles.dropdownIndicator,
7991
- tooltip: i18n.t("grafana-scenes.variables.adhoc-filters-combobox-renderer.expand-filters", "Expand filters"),
7992
- onClick: (e) => {
7993
- e.stopPropagation();
7994
- handleExpand();
7995
- }
7996
- }
7997
- ), !readOnly && /* @__PURE__ */ React__default.default.createElement(
7998
- ui.IconButton,
7999
- {
8000
- name: "times",
8001
- size: "md",
8002
- className: styles.controlButton,
8003
- tooltip: i18n.t("grafana-scenes.variables.adhoc-filters-combobox-renderer.clear-all", "Clear all"),
8004
- onClick: clearAll
8005
- }
8006
- ))
8007
- );
8008
- });
7872
+ placement: "bottom-start",
7873
+ referenceElement: ref.current,
7874
+ show: true
7875
+ }
7876
+ ));
7877
+ }
8009
7878
  const getStyles$c = (theme) => ({
8010
- comboboxWrapper: css.css({
7879
+ menuContainer: css.css({
8011
7880
  display: "flex",
8012
- flexWrap: "wrap",
8013
- alignItems: "center",
8014
- columnGap: theme.spacing(1),
8015
- rowGap: theme.spacing(0.5),
8016
- minHeight: theme.spacing(4),
8017
- backgroundColor: theme.components.input.background,
8018
- border: `1px solid ${theme.colors.border.strong}`,
7881
+ flexDirection: "column",
7882
+ background: theme.colors.background.elevated,
7883
+ border: `1px solid ${theme.colors.border.weak}`,
8019
7884
  borderRadius: theme.shape.radius.default,
8020
- paddingInline: theme.spacing(1),
8021
- paddingBlock: theme.spacing(0.5),
8022
- flexGrow: 1,
8023
- width: "100%"
8024
- }),
8025
- comboboxFocusOutline: css.css({
8026
- "&:focus-within": {
8027
- outline: "2px dotted transparent",
8028
- outlineOffset: "2px",
8029
- boxShadow: `0 0 0 2px ${theme.colors.background.canvas}, 0 0 0px 4px ${theme.colors.primary.main}`,
8030
- transitionTimingFunction: `cubic-bezier(0.19, 1, 0.22, 1)`,
8031
- transitionDuration: "0.2s",
8032
- transitionProperty: "outline, outline-offset, box-shadow",
8033
- zIndex: 2
8034
- }
8035
- }),
8036
- collapsed: css.css({
8037
- flexWrap: "nowrap",
8038
- overflow: "hidden"
7885
+ boxShadow: theme.shadows.z3,
7886
+ padding: theme.spacing(2)
8039
7887
  }),
8040
- rightControls: css.css({
8041
- display: "flex",
7888
+ combinedFilterPill: css.css({
8042
7889
  alignItems: "center",
8043
- marginLeft: "auto",
8044
- flexShrink: 0,
8045
- gap: theme.spacing(1.5)
8046
- }),
8047
- moreIndicator: css.css({
8048
- color: theme.colors.text.primary,
8049
7890
  background: theme.colors.action.selected,
8050
- height: "auto",
8051
- lineHeight: "normal",
8052
- alignSelf: "stretch",
7891
+ borderRadius: theme.shape.radius.default,
7892
+ border: `1px solid ${theme.colors.border.weak}`,
7893
+ padding: theme.spacing(0.2, 1),
7894
+ color: theme.colors.text.primary,
7895
+ overflow: "hidden",
7896
+ whiteSpace: "nowrap",
7897
+ minHeight: theme.spacing(2.75),
7898
+ ...theme.typography.bodySmall,
7899
+ fontWeight: theme.typography.fontWeightBold,
7900
+ cursor: "pointer",
8053
7901
  "&:hover": {
8054
7902
  background: theme.colors.action.hover
8055
7903
  }
8056
7904
  }),
8057
- dropdownIndicator: css.css({
8058
- color: theme.colors.text.secondary,
8059
- flexShrink: 0
8060
- }),
8061
- collapseButton: css.css({
8062
- color: theme.colors.text.secondary,
8063
- padding: 0,
8064
- height: "auto",
8065
- lineHeight: "normal",
8066
- "&:hover": {
8067
- background: "transparent",
8068
- color: theme.colors.text.primary
7905
+ iconActive: css.css({
7906
+ "&:before": {
7907
+ backgroundColor: theme.colors.action.hover,
7908
+ opacity: 1
8069
7909
  }
8070
7910
  }),
8071
- sectionDivider: css.css({
8072
- width: "1px",
8073
- alignSelf: "stretch",
8074
- backgroundColor: theme.colors.border.weak,
8075
- flexShrink: 0
8076
- }),
8077
- groupByLabel: css.css({
8078
- ...theme.typography.bodySmall,
8079
- fontWeight: theme.typography.fontWeightBold,
8080
- color: theme.colors.text.primary,
8081
- whiteSpace: "nowrap"
8082
- }),
8083
- controlButton: css.css({
8084
- color: theme.colors.text.secondary,
8085
- "&:hover": {
8086
- color: theme.colors.text.primary
8087
- }
8088
- })
8089
- });
8090
-
8091
- var __typeError$3 = (msg) => {
8092
- throw TypeError(msg);
8093
- };
8094
- var __accessCheck$3 = (obj, member, msg) => member.has(obj) || __typeError$3("Cannot " + msg);
8095
- var __privateGet$3 = (obj, member, getter) => (__accessCheck$3(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
8096
- var __privateAdd$3 = (obj, member, value) => member.has(obj) ? __typeError$3("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
8097
- var __privateSet$3 = (obj, member, value, setter) => (__accessCheck$3(obj, member, "write to private field"), member.set(obj, value), value);
8098
- var _value;
8099
- class SafeSerializableSceneObject {
8100
- constructor(value) {
8101
- __privateAdd$3(this, _value);
8102
- this.text = "__sceneObject";
8103
- this.valueOf = () => {
8104
- return __privateGet$3(this, _value);
8105
- };
8106
- __privateSet$3(this, _value, value);
8107
- }
8108
- toString() {
8109
- return void 0;
8110
- }
8111
- get value() {
8112
- return this;
8113
- }
8114
- }
8115
- _value = new WeakMap();
8116
-
8117
- function shouldWrapInSafeSerializableSceneObject(grafanaVersion) {
8118
- const pattern = /^(\d+)\.(\d+)\.(\d+)/;
8119
- const match = grafanaVersion.match(pattern);
8120
- if (!match) {
8121
- return false;
8122
- }
8123
- const major = parseInt(match[1], 10);
8124
- const minor = parseInt(match[2], 10);
8125
- const patch = parseInt(match[3], 10);
8126
- if (major === 11) {
8127
- return minor === 0 && patch >= 4 || minor === 1 && patch >= 2 || minor > 1;
8128
- }
8129
- if (major === 10) {
8130
- return minor === 4 && patch >= 8 || minor >= 5;
8131
- }
8132
- return major > 11;
8133
- }
8134
- function wrapInSafeSerializableSceneObject(sceneObject) {
8135
- const version = runtime.config.buildInfo.version;
8136
- if (shouldWrapInSafeSerializableSceneObject(version)) {
8137
- return new SafeSerializableSceneObject(sceneObject);
8138
- }
8139
- return { value: sceneObject, text: "__sceneObject" };
8140
- }
8141
-
8142
- const reverseScopeFilterOperatorMap = Object.fromEntries(
8143
- Object.entries(data.scopeFilterOperatorMap).map(([symbol, operator]) => [operator, symbol])
8144
- );
8145
- function isEqualityOrMultiOperator(value) {
8146
- const operators = /* @__PURE__ */ new Set(["equals", "not-equals", "one-of", "not-one-of"]);
8147
- return operators.has(value);
8148
- }
8149
- function isRegexOperator(value) {
8150
- const operators = /* @__PURE__ */ new Set(["regex-match", "regex-not-match"]);
8151
- return operators.has(value);
8152
- }
8153
- function getAdHocFiltersFromScopes(scopes) {
8154
- const formattedFilters = /* @__PURE__ */ new Map();
8155
- const duplicatedFilters = [];
8156
- const allFilters = scopes.flatMap((scope) => scope.spec.filters);
8157
- for (const filter of allFilters) {
8158
- processFilter(formattedFilters, duplicatedFilters, filter);
8159
- }
8160
- return [...formattedFilters.values(), ...duplicatedFilters];
8161
- }
8162
- function processFilter(formattedFilters, duplicatedFilters, filter) {
8163
- var _a, _b;
8164
- if (!filter) {
8165
- return;
8166
- }
8167
- const existingFilter = formattedFilters.get(filter.key);
8168
- if (existingFilter && isEqualityValue(existingFilter.operator, filter.operator)) {
8169
- mergeFilterValues(existingFilter, filter);
8170
- } else if (existingFilter && isRegexValue(existingFilter.operator, filter.operator)) {
8171
- existingFilter.value += `|${filter.value}`;
8172
- existingFilter.values = [existingFilter.value];
8173
- } else if (!existingFilter) {
8174
- formattedFilters.set(filter.key, {
8175
- key: filter.key,
8176
- operator: reverseScopeFilterOperatorMap[filter.operator],
8177
- value: filter.value,
8178
- values: (_a = filter.values) != null ? _a : [filter.value],
8179
- origin: "scope"
8180
- });
8181
- } else {
8182
- duplicatedFilters.push({
8183
- key: filter.key,
8184
- operator: reverseScopeFilterOperatorMap[filter.operator],
8185
- value: filter.value,
8186
- values: (_b = filter.values) != null ? _b : [filter.value],
8187
- origin: "scope"
8188
- });
8189
- }
8190
- }
8191
- function mergeFilterValues(adHocFilter, filter) {
8192
- var _a, _b, _c, _d;
8193
- const values = (_a = filter.values) != null ? _a : [filter.value];
8194
- for (const value of values) {
8195
- if (!((_b = adHocFilter.values) == null ? void 0 : _b.includes(value))) {
8196
- (_c = adHocFilter.values) == null ? void 0 : _c.push(value);
8197
- }
7911
+ emptyMessage: css.css({
7912
+ padding: theme.spacing(0.5, 0),
7913
+ color: theme.colors.text.secondary,
7914
+ ...theme.typography.bodySmall
7915
+ })
7916
+ });
7917
+
7918
+ var __typeError$3 = (msg) => {
7919
+ throw TypeError(msg);
7920
+ };
7921
+ var __accessCheck$3 = (obj, member, msg) => member.has(obj) || __typeError$3("Cannot " + msg);
7922
+ var __privateGet$3 = (obj, member, getter) => (__accessCheck$3(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
7923
+ var __privateAdd$3 = (obj, member, value) => member.has(obj) ? __typeError$3("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
7924
+ var __privateSet$3 = (obj, member, value, setter) => (__accessCheck$3(obj, member, "write to private field"), member.set(obj, value), value);
7925
+ var _value;
7926
+ class SafeSerializableSceneObject {
7927
+ constructor(value) {
7928
+ __privateAdd$3(this, _value);
7929
+ this.text = "__sceneObject";
7930
+ this.valueOf = () => {
7931
+ return __privateGet$3(this, _value);
7932
+ };
7933
+ __privateSet$3(this, _value, value);
8198
7934
  }
8199
- if (((_d = adHocFilter.values) == null ? void 0 : _d.length) === 1) {
8200
- return;
7935
+ toString() {
7936
+ return void 0;
8201
7937
  }
8202
- if (filter.operator === "equals" && adHocFilter.operator === reverseScopeFilterOperatorMap["equals"]) {
8203
- adHocFilter.operator = reverseScopeFilterOperatorMap["one-of"];
8204
- } else if (filter.operator === "not-equals" && adHocFilter.operator === reverseScopeFilterOperatorMap["not-equals"]) {
8205
- adHocFilter.operator = reverseScopeFilterOperatorMap["not-one-of"];
7938
+ get value() {
7939
+ return this;
8206
7940
  }
8207
7941
  }
8208
- function isRegexValue(adHocFilterOperator, filterOperator) {
8209
- const scopeConvertedOperator = data.scopeFilterOperatorMap[adHocFilterOperator];
8210
- if (!isRegexOperator(scopeConvertedOperator) || !isRegexOperator(filterOperator)) {
7942
+ _value = new WeakMap();
7943
+
7944
+ function shouldWrapInSafeSerializableSceneObject(grafanaVersion) {
7945
+ const pattern = /^(\d+)\.(\d+)\.(\d+)/;
7946
+ const match = grafanaVersion.match(pattern);
7947
+ if (!match) {
8211
7948
  return false;
8212
7949
  }
8213
- return hasSameOperators(scopeConvertedOperator, filterOperator);
8214
- }
8215
- function isEqualityValue(adHocFilterOperator, filterOperator) {
8216
- const scopeConvertedOperator = data.scopeFilterOperatorMap[adHocFilterOperator];
8217
- if (!isEqualityOrMultiOperator(scopeConvertedOperator) || !isEqualityOrMultiOperator(filterOperator)) {
8218
- return false;
7950
+ const major = parseInt(match[1], 10);
7951
+ const minor = parseInt(match[2], 10);
7952
+ const patch = parseInt(match[3], 10);
7953
+ if (major === 11) {
7954
+ return minor === 0 && patch >= 4 || minor === 1 && patch >= 2 || minor > 1;
8219
7955
  }
8220
- return hasSameOperators(scopeConvertedOperator, filterOperator);
7956
+ if (major === 10) {
7957
+ return minor === 4 && patch >= 8 || minor >= 5;
7958
+ }
7959
+ return major > 11;
8221
7960
  }
8222
- function hasSameOperators(scopeConvertedOperator, filterOperator) {
8223
- if (scopeConvertedOperator.includes("not") && !filterOperator.includes("not") || !scopeConvertedOperator.includes("not") && filterOperator.includes("not")) {
8224
- return false;
7961
+ function wrapInSafeSerializableSceneObject(sceneObject) {
7962
+ const version = runtime.config.buildInfo.version;
7963
+ if (shouldWrapInSafeSerializableSceneObject(version)) {
7964
+ return new SafeSerializableSceneObject(sceneObject);
8225
7965
  }
8226
- return true;
7966
+ return { value: sceneObject, text: "__sceneObject" };
8227
7967
  }
8228
7968
 
8229
- function isInteractionTracker(s) {
8230
- return "isInteractionTracker" in s;
7969
+ const getRecentFiltersKey = (datasourceUid) => `grafana.filters.recent.${datasourceUid != null ? datasourceUid : "default"}`;
7970
+ const getRecentGroupingKey$1 = (datasourceUid) => `grafana.grouping.recent.${datasourceUid != null ? datasourceUid : "default"}`;
7971
+ function getFilterIdentity(filter) {
7972
+ return `${filter.key}|${filter.operator}|${filter.value}`;
8231
7973
  }
8232
- class SceneInteractionTracker extends SceneObjectBase {
8233
- constructor(state = {}, renderProfiler) {
8234
- super(state);
8235
- this.renderProfiler = renderProfiler;
8236
- this.isInteractionTracker = true;
8237
- if (renderProfiler) {
8238
- this.renderProfiler = renderProfiler;
8239
- this.renderProfiler.setInteractionCompleteHandler(state.onInteractionComplete);
7974
+ function deduplicateFilters(filters) {
7975
+ const filterMap = /* @__PURE__ */ new Map();
7976
+ for (const filter of filters) {
7977
+ const identity = getFilterIdentity(filter);
7978
+ if (filterMap.has(identity)) {
7979
+ filterMap.delete(identity);
8240
7980
  }
7981
+ filterMap.set(identity, filter);
8241
7982
  }
8242
- startInteraction(name) {
8243
- var _a;
8244
- if (!this.state.enableInteractionTracking) {
8245
- return;
7983
+ return Array.from(filterMap.values());
7984
+ }
7985
+ function deduplicateGroupings(groupings) {
7986
+ const seen = /* @__PURE__ */ new Map();
7987
+ for (const g of groupings) {
7988
+ const key = String(g.value);
7989
+ if (seen.has(key)) {
7990
+ seen.delete(key);
8246
7991
  }
8247
- (_a = this.renderProfiler) == null ? void 0 : _a.startInteraction(name);
8248
- }
8249
- stopInteraction() {
8250
- var _a;
8251
- (_a = this.renderProfiler) == null ? void 0 : _a.stopInteraction();
7992
+ seen.set(key, g);
8252
7993
  }
7994
+ return Array.from(seen.values());
8253
7995
  }
8254
-
8255
- function getInteractionTracker(sceneObject) {
8256
- let parent = sceneObject;
8257
- while (parent) {
8258
- if (parent.state.$behaviors) {
8259
- for (const behavior of parent.state.$behaviors) {
8260
- if (isInteractionTracker(behavior)) {
8261
- return behavior;
7996
+ class AdHocFiltersRecommendations extends SceneObjectBase {
7997
+ constructor(state = {}) {
7998
+ super(state);
7999
+ this._activationHandler = () => {
8000
+ const filterJson = data.store.get(this._getFiltersStorageKey());
8001
+ const storedFilters = filterJson ? JSON.parse(filterJson) : [];
8002
+ if (storedFilters.length > 0) {
8003
+ this._verifyRecentFiltersApplicability(storedFilters);
8004
+ } else {
8005
+ this.setState({ recentFilters: [] });
8006
+ }
8007
+ if (this._isGroupByEnabled) {
8008
+ const groupingJson = data.store.get(this._getGroupingStorageKey());
8009
+ const storedGroupings = groupingJson ? JSON.parse(groupingJson) : [];
8010
+ if (storedGroupings.length > 0) {
8011
+ this._verifyRecentGroupingsApplicability(storedGroupings);
8012
+ } else {
8013
+ this.setState({ recentGrouping: [] });
8262
8014
  }
8263
8015
  }
8264
- }
8265
- parent = parent.parent;
8266
- }
8267
- return void 0;
8268
- }
8269
-
8270
- class AdHocFiltersVariableController {
8271
- constructor(model) {
8272
- this.model = model;
8273
- }
8274
- useState() {
8275
- const state = this.model.useState();
8276
- return {
8277
- filters: state.filters,
8278
- originFilters: state.originFilters,
8279
- readOnly: state.readOnly,
8280
- allowCustomValue: state.allowCustomValue,
8281
- supportsMultiValueOperators: state.supportsMultiValueOperators,
8282
- onAddCustomValue: state.onAddCustomValue,
8283
- wip: state._wip,
8284
- inputPlaceholder: state.inputPlaceholder,
8285
- groupByInputPlaceholder: state.groupByInputPlaceholder,
8286
- collapsible: state.collapsible,
8287
- valueRecommendations: this.model.getRecommendations(),
8288
- drilldownRecommendationsEnabled: state.drilldownRecommendationsEnabled,
8289
- enableGroupBy: state.enableGroupBy,
8290
- groupByRestorable: this.model.isGroupByRestorable()
8016
+ this._fetchRecommendedDrilldowns();
8017
+ const scopesVariable = sceneGraph.lookupVariable(SCOPES_VARIABLE_NAME, this._adHocFilter);
8018
+ let scopesSubscription;
8019
+ let adHocSubscription;
8020
+ if (scopesVariable instanceof ScopesVariable) {
8021
+ this._subs.add(
8022
+ scopesSubscription = scopesVariable.subscribeToState((newState, prevState) => {
8023
+ if (newState.scopes !== prevState.scopes) {
8024
+ this._reloadStoredFilters();
8025
+ if (this._isGroupByEnabled) {
8026
+ this._reloadStoredGroupings();
8027
+ }
8028
+ this._fetchRecommendedDrilldowns();
8029
+ }
8030
+ })
8031
+ );
8032
+ }
8033
+ this._subs.add(
8034
+ adHocSubscription = this._adHocFilter.subscribeToState((newState, prevState) => {
8035
+ if (newState.filters !== prevState.filters) {
8036
+ this._reloadStoredFilters();
8037
+ if (this._isGroupByEnabled) {
8038
+ this._reloadStoredGroupings();
8039
+ }
8040
+ this._fetchRecommendedDrilldowns();
8041
+ }
8042
+ })
8043
+ );
8044
+ return () => {
8045
+ scopesSubscription == null ? void 0 : scopesSubscription.unsubscribe();
8046
+ adHocSubscription == null ? void 0 : adHocSubscription.unsubscribe();
8047
+ };
8291
8048
  };
8049
+ this.addActivationHandler(this._activationHandler);
8292
8050
  }
8293
- async getKeys(currentKey) {
8294
- return this.model._getKeys(currentKey);
8295
- }
8296
- async getGroupByKeys(currentKey) {
8297
- return this.model._getGroupByKeys(currentKey);
8298
- }
8299
- async getValuesFor(filter) {
8300
- return this.model._getValuesFor(filter);
8301
- }
8302
- getOperators() {
8303
- return this.model._getOperators();
8304
- }
8305
- updateFilter(filter, update) {
8306
- this.model._updateFilter(filter, update);
8051
+ get _adHocFilter() {
8052
+ if (!(this.parent instanceof AdHocFiltersVariable)) {
8053
+ throw new Error("AdHocFiltersRecommendations must be a child of AdHocFiltersVariable");
8054
+ }
8055
+ return this.parent;
8307
8056
  }
8308
- updateFilters(filters, options) {
8309
- this.model.updateFilters(filters, options);
8057
+ get _scopedVars() {
8058
+ return { __sceneObject: wrapInSafeSerializableSceneObject(this._adHocFilter) };
8310
8059
  }
8311
- updateToMatchAll(filter) {
8312
- this.model.updateToMatchAll(filter);
8060
+ get _isGroupByEnabled() {
8061
+ return this._adHocFilter.state.enableGroupBy === true;
8313
8062
  }
8314
- removeFilter(filter) {
8315
- this.model._removeFilter(filter);
8063
+ _reloadStoredFilters() {
8064
+ const json = data.store.get(this._getFiltersStorageKey());
8065
+ const storedFilters = json ? JSON.parse(json) : [];
8066
+ if (storedFilters.length > 0) {
8067
+ this._verifyRecentFiltersApplicability(storedFilters);
8068
+ }
8316
8069
  }
8317
- removeLastFilter() {
8318
- this.model._removeLastFilter();
8070
+ _reloadStoredGroupings() {
8071
+ const json = data.store.get(this._getGroupingStorageKey());
8072
+ const storedGroupings = json ? JSON.parse(json) : [];
8073
+ if (storedGroupings.length > 0) {
8074
+ this._verifyRecentGroupingsApplicability(storedGroupings);
8075
+ }
8319
8076
  }
8320
- handleComboboxBackspace(filter) {
8321
- this.model._handleComboboxBackspace(filter);
8077
+ _getFiltersStorageKey() {
8078
+ var _a;
8079
+ return getRecentFiltersKey((_a = this._adHocFilter.state.datasource) == null ? void 0 : _a.uid);
8322
8080
  }
8323
- addWip() {
8324
- this.model._addWip();
8081
+ _getGroupingStorageKey() {
8082
+ var _a;
8083
+ return getRecentGroupingKey$1((_a = this._adHocFilter.state.datasource) == null ? void 0 : _a.uid);
8325
8084
  }
8326
- addGroupByFilter(item) {
8327
- this.model._addGroupByFilter(item);
8085
+ async _fetchRecommendedDrilldowns() {
8086
+ var _a;
8087
+ const adhoc = this._adHocFilter;
8088
+ const ds = await getDataSource(adhoc.state.datasource, this._scopedVars);
8089
+ if (!ds || !ds.getRecommendedDrilldowns) {
8090
+ this.setState({ datasourceSupportsRecommendations: false });
8091
+ return;
8092
+ }
8093
+ this.setState({ datasourceSupportsRecommendations: true });
8094
+ const queries = adhoc.state.useQueriesAsFilterForOptions ? getQueriesForVariables(adhoc) : void 0;
8095
+ const timeRange = sceneGraph.getTimeRange(adhoc).state.value;
8096
+ const scopes = sceneGraph.getScopes(adhoc);
8097
+ const allFilters = [...(_a = adhoc.state.originFilters) != null ? _a : [], ...adhoc.state.filters];
8098
+ const filters = allFilters.filter((f) => !isGroupByFilter(f));
8099
+ const groupByKeys = this._isGroupByEnabled ? allFilters.filter((f) => isGroupByFilter(f)).map((f) => f.key) : void 0;
8100
+ const enrichedRequest = getEnrichedDataRequest(adhoc);
8101
+ const dashboardUid = enrichedRequest == null ? void 0 : enrichedRequest.dashboardUID;
8102
+ try {
8103
+ const recommendedDrilldowns = await ds.getRecommendedDrilldowns({
8104
+ timeRange,
8105
+ dashboardUid,
8106
+ queries: queries != null ? queries : [],
8107
+ filters,
8108
+ ...groupByKeys ? { groupByKeys } : {},
8109
+ scopes
8110
+ });
8111
+ const stateUpdate = {};
8112
+ if (recommendedDrilldowns == null ? void 0 : recommendedDrilldowns.filters) {
8113
+ stateUpdate.recommendedFilters = recommendedDrilldowns.filters;
8114
+ }
8115
+ if (this._isGroupByEnabled && (recommendedDrilldowns == null ? void 0 : recommendedDrilldowns.groupByKeys)) {
8116
+ stateUpdate.recommendedGrouping = recommendedDrilldowns.groupByKeys.map((key) => ({
8117
+ value: key,
8118
+ text: key
8119
+ }));
8120
+ }
8121
+ this.setState(stateUpdate);
8122
+ } catch (error) {
8123
+ console.error("Failed to fetch recommended drilldowns:", error);
8124
+ }
8328
8125
  }
8329
- restoreOriginalFilter(filter) {
8330
- this.model.restoreOriginalFilter(filter);
8126
+ async _verifyRecentFiltersApplicability(storedFilters) {
8127
+ const adhoc = this._adHocFilter;
8128
+ const queries = adhoc.state.useQueriesAsFilterForOptions ? getQueriesForVariables(adhoc) : void 0;
8129
+ const response = await adhoc.getFiltersApplicabilityForQueries(storedFilters, queries != null ? queries : []);
8130
+ if (!response) {
8131
+ const deduped = deduplicateFilters(storedFilters);
8132
+ this.setState({ recentFilters: deduped.slice(-3) });
8133
+ return;
8134
+ }
8135
+ const applicabilityMap = /* @__PURE__ */ new Map();
8136
+ response.forEach((item) => {
8137
+ applicabilityMap.set(item.key, item.applicable !== false);
8138
+ });
8139
+ const applicableFilters = storedFilters.filter((f) => {
8140
+ const isApplicable = applicabilityMap.get(f.key);
8141
+ return isApplicable === void 0 || isApplicable === true;
8142
+ });
8143
+ const recentFilters = deduplicateFilters(applicableFilters).slice(-3);
8144
+ this.setState({ recentFilters });
8331
8145
  }
8332
- restoreOriginalGroupBy() {
8333
- this.model.restoreOriginalGroupBy();
8146
+ async _verifyRecentGroupingsApplicability(storedGroupings) {
8147
+ const adhoc = this._adHocFilter;
8148
+ const queries = adhoc.state.useQueriesAsFilterForOptions ? getQueriesForVariables(adhoc) : void 0;
8149
+ const groupByFilters = storedGroupings.map((g) => ({
8150
+ key: String(g.value),
8151
+ operator: GROUP_BY_OPERATOR,
8152
+ value: "",
8153
+ condition: ""
8154
+ }));
8155
+ const response = await adhoc.getFiltersApplicabilityForQueries(groupByFilters, queries != null ? queries : []);
8156
+ if (!response) {
8157
+ this.setState({ recentGrouping: deduplicateGroupings(storedGroupings).slice(-3) });
8158
+ return;
8159
+ }
8160
+ const applicabilityMap = /* @__PURE__ */ new Map();
8161
+ response.forEach((item) => {
8162
+ applicabilityMap.set(item.key, item.applicable !== false);
8163
+ });
8164
+ const applicableGroupings = deduplicateGroupings(storedGroupings).filter((g) => {
8165
+ const isApplicable = applicabilityMap.get(String(g.value));
8166
+ return isApplicable === void 0 || isApplicable === true;
8167
+ }).slice(-3);
8168
+ this.setState({ recentGrouping: applicableGroupings });
8334
8169
  }
8335
- clearAll() {
8336
- this.model.clearAll();
8170
+ /**
8171
+ * Stores a recent filter in localStorage and updates state.
8172
+ */
8173
+ storeRecentFilter(filter) {
8174
+ const key = this._getFiltersStorageKey();
8175
+ const storedFilters = data.store.get(key);
8176
+ const allRecentFilters = storedFilters ? JSON.parse(storedFilters) : [];
8177
+ const updatedStoredFilters = deduplicateFilters([...allRecentFilters, filter]).slice(-10);
8178
+ data.store.set(key, JSON.stringify(updatedStoredFilters));
8179
+ const adhoc = this._adHocFilter;
8180
+ const existingFilter = adhoc.state.filters.find((f) => f.key === filter.key && !Boolean(f.nonApplicable));
8181
+ if (existingFilter && !Boolean(existingFilter.nonApplicable)) {
8182
+ this.setState({ recentFilters: updatedStoredFilters.slice(-3) });
8183
+ }
8337
8184
  }
8338
- startProfile(name) {
8339
- const queryController = getQueryController(this.model);
8340
- queryController == null ? void 0 : queryController.startProfile(name);
8185
+ /**
8186
+ * Stores a recent grouping key in localStorage and updates state.
8187
+ * No-op when enableGroupBy is false.
8188
+ */
8189
+ storeRecentGrouping(groupByKey) {
8190
+ if (!this._isGroupByEnabled) {
8191
+ return;
8192
+ }
8193
+ const storageKey = this._getGroupingStorageKey();
8194
+ const storedGroupings = data.store.get(storageKey);
8195
+ const allRecentGroupings = storedGroupings ? JSON.parse(storedGroupings) : [];
8196
+ const withoutDuplicate = allRecentGroupings.filter((g) => String(g.value) !== groupByKey);
8197
+ const updated = [...withoutDuplicate, { value: groupByKey, text: groupByKey }];
8198
+ const limited = updated.slice(-10);
8199
+ data.store.set(storageKey, JSON.stringify(limited));
8200
+ this.setState({ recentGrouping: limited.slice(-3) });
8341
8201
  }
8342
- startInteraction(name) {
8343
- const interactionTracker = getInteractionTracker(this.model);
8344
- interactionTracker == null ? void 0 : interactionTracker.startInteraction(name);
8202
+ addFilterToParent(filter) {
8203
+ this._adHocFilter.updateFilters([...this._adHocFilter.state.filters, filter]);
8345
8204
  }
8346
- stopInteraction() {
8347
- const interactionTracker = getInteractionTracker(this.model);
8348
- interactionTracker == null ? void 0 : interactionTracker.stopInteraction();
8205
+ addGroupByToParent(key) {
8206
+ if (!this._isGroupByEnabled) {
8207
+ return;
8208
+ }
8209
+ const adhoc = this._adHocFilter;
8210
+ const exists = adhoc.state.filters.some((f) => isGroupByFilter(f) && f.key === key);
8211
+ if (exists) {
8212
+ return;
8213
+ }
8214
+ adhoc._addGroupByFilter({ value: key, label: key });
8349
8215
  }
8350
8216
  }
8217
+ AdHocFiltersRecommendations.Component = AdHocFiltersRecommendationsRenderer;
8218
+ function AdHocFiltersRecommendationsRenderer({ model }) {
8219
+ const { recentFilters, recommendedFilters, datasourceSupportsRecommendations } = model.useState();
8220
+ const { filters } = model._adHocFilter.useState();
8221
+ const recentDrilldowns = recentFilters == null ? void 0 : recentFilters.map((filter) => ({
8222
+ label: `${filter.key} ${filter.operator} ${filter.value}`,
8223
+ onClick: () => {
8224
+ const exists = filters.some((f) => f.key === filter.key && f.value === filter.value);
8225
+ if (!exists) {
8226
+ model.addFilterToParent(filter);
8227
+ }
8228
+ }
8229
+ }));
8230
+ const recommendedDrilldowns = recommendedFilters == null ? void 0 : recommendedFilters.map((filter) => ({
8231
+ label: `${filter.key} ${filter.operator} ${filter.value}`,
8232
+ onClick: () => {
8233
+ const exists = filters.some((f) => f.key === filter.key && f.value === filter.value);
8234
+ if (!exists) {
8235
+ model.addFilterToParent(filter);
8236
+ }
8237
+ }
8238
+ }));
8239
+ return /* @__PURE__ */ React__default.default.createElement(
8240
+ DrilldownRecommendations,
8241
+ {
8242
+ recentDrilldowns,
8243
+ recommendedDrilldowns,
8244
+ showRecommended: datasourceSupportsRecommendations
8245
+ }
8246
+ );
8247
+ }
8248
+ function AdHocGroupByRecommendationsRenderer({ model }) {
8249
+ const { recentGrouping, recommendedGrouping, datasourceSupportsRecommendations } = model.useState();
8250
+ const recentDrilldowns = recentGrouping == null ? void 0 : recentGrouping.map((groupBy) => ({
8251
+ label: `${groupBy.value}`,
8252
+ onClick: () => {
8253
+ model.addGroupByToParent(String(groupBy.value));
8254
+ }
8255
+ }));
8256
+ const recommendedDrilldowns = recommendedGrouping == null ? void 0 : recommendedGrouping.map((groupBy) => ({
8257
+ label: `${groupBy.value}`,
8258
+ onClick: () => {
8259
+ model.addGroupByToParent(String(groupBy.value));
8260
+ }
8261
+ }));
8262
+ return /* @__PURE__ */ React__default.default.createElement(
8263
+ DrilldownRecommendations,
8264
+ {
8265
+ recentDrilldowns,
8266
+ recommendedDrilldowns,
8267
+ showRecommended: datasourceSupportsRecommendations
8268
+ }
8269
+ );
8270
+ }
8351
8271
 
8352
- function DrilldownRecommendations({ recentDrilldowns, recommendedDrilldowns, showRecommended = false }) {
8272
+ const MAX_VISIBLE_FILTERS_DEFAULT = 4;
8273
+ const MAX_VISIBLE_FILTERS_WITH_GROUP_BY = 2;
8274
+ const MAX_VISIBLE_GROUP_BY = 2;
8275
+ const AdHocFiltersComboboxRenderer = React.memo(function AdHocFiltersComboboxRenderer2({ controller }) {
8276
+ var _a;
8277
+ const { originFilters, filters, readOnly, collapsible, valueRecommendations, enableGroupBy, groupByRestorable } = controller.useState();
8353
8278
  const styles = ui.useStyles2(getStyles$b);
8354
- const [isPopoverVisible, setPopoverVisible] = React.useState(false);
8355
- const ref = React.useRef(null);
8356
- const openPopover = () => {
8357
- setPopoverVisible(true);
8279
+ const theme = ui.useTheme2();
8280
+ const [collapsed, setCollapsed] = React.useState(true);
8281
+ const [wrapperRef, { height: wrapperHeight }] = reactUse.useMeasure();
8282
+ const clearAll = () => {
8283
+ var _a2;
8284
+ (_a2 = controller.clearAll) == null ? void 0 : _a2.call(controller);
8358
8285
  };
8359
- const onClickAction = (action) => {
8360
- action();
8361
- setPopoverVisible(false);
8286
+ const focusOnWipInputRef = React.useRef();
8287
+ const focusOnGroupByWipInputRef = React.useRef();
8288
+ const singleLineThreshold = theme.spacing.gridSize * 5;
8289
+ const isMultiLine = collapsible && wrapperHeight > singleLineThreshold;
8290
+ const handleCollapseToggle = (event) => {
8291
+ event.stopPropagation();
8292
+ if (collapsible) {
8293
+ setCollapsed(true);
8294
+ }
8362
8295
  };
8363
- const content = /* @__PURE__ */ React__default.default.createElement(ui.ClickOutsideWrapper, { onClick: () => setPopoverVisible(false), useCapture: true }, /* @__PURE__ */ React__default.default.createElement("div", { className: styles.menuContainer, onClick: (ev) => ev.stopPropagation() }, /* @__PURE__ */ React__default.default.createElement(ui.Stack, { direction: "column" }, /* @__PURE__ */ React__default.default.createElement(ui.Text, { weight: "bold", variant: "bodySmall", color: "secondary" }, /* @__PURE__ */ React__default.default.createElement(i18n.Trans, { i18nKey: "grafana-scenes.components.drilldown-recommendations.recent" }, "Recent")), recentDrilldowns && recentDrilldowns.length > 0 ? recentDrilldowns.map((drilldown) => /* @__PURE__ */ React__default.default.createElement(
8364
- "div",
8365
- {
8366
- key: drilldown.label,
8367
- className: css.cx(styles.combinedFilterPill),
8368
- onClick: () => onClickAction(drilldown.onClick)
8369
- },
8370
- drilldown.label
8371
- )) : /* @__PURE__ */ React__default.default.createElement("div", { className: styles.emptyMessage }, /* @__PURE__ */ React__default.default.createElement(i18n.Trans, { i18nKey: "grafana-scenes.components.drilldown-recommendations.recent-empty" }, "No recent values")), showRecommended && /* @__PURE__ */ React__default.default.createElement(React__default.default.Fragment, null, /* @__PURE__ */ React__default.default.createElement(ui.Text, { weight: "bold", variant: "bodySmall", color: "secondary" }, /* @__PURE__ */ React__default.default.createElement(i18n.Trans, { i18nKey: "grafana-scenes.components.drilldown-recommendations.recommended" }, "Recommended")), recommendedDrilldowns && recommendedDrilldowns.length > 0 ? recommendedDrilldowns.map((drilldown) => /* @__PURE__ */ React__default.default.createElement(
8296
+ const handleExpand = () => {
8297
+ if (collapsible && collapsed) {
8298
+ setCollapsed(false);
8299
+ }
8300
+ };
8301
+ const visibleOriginFilters = (_a = originFilters == null ? void 0 : originFilters.filter((f) => f.origin && !f.hidden && !f.dismissedGroupBy)) != null ? _a : [];
8302
+ const visibleFilters = filters.filter((f) => !f.hidden);
8303
+ const allFilters = [...visibleOriginFilters, ...visibleFilters];
8304
+ const totalFiltersCount = allFilters.length;
8305
+ const adhocFilters = allFilters.filter((f) => !isGroupByFilter(f));
8306
+ const groupByFilters = allFilters.filter(isGroupByFilter);
8307
+ const shouldCollapse = collapsible && collapsed && totalFiltersCount > 0;
8308
+ const maxVisibleAdhocFilters = enableGroupBy ? MAX_VISIBLE_FILTERS_WITH_GROUP_BY : MAX_VISIBLE_FILTERS_DEFAULT;
8309
+ const adhocFiltersToRender = shouldCollapse ? adhocFilters.slice(0, maxVisibleAdhocFilters) : adhocFilters;
8310
+ const adhocHiddenCount = shouldCollapse ? Math.max(0, adhocFilters.length - maxVisibleAdhocFilters) : 0;
8311
+ const groupByFiltersToRender = shouldCollapse ? groupByFilters.slice(0, MAX_VISIBLE_GROUP_BY) : groupByFilters;
8312
+ const groupByHiddenCount = shouldCollapse ? Math.max(0, groupByFilters.length - MAX_VISIBLE_GROUP_BY) : 0;
8313
+ React.useEffect(() => {
8314
+ if (collapsible && totalFiltersCount === 0 && collapsed) {
8315
+ setCollapsed(false);
8316
+ }
8317
+ }, [collapsible, totalFiltersCount, collapsed]);
8318
+ const showCollapseButton = collapsible && isMultiLine && !collapsed;
8319
+ const showExpandButton = shouldCollapse && (adhocHiddenCount > 0 || groupByHiddenCount > 0);
8320
+ return /* @__PURE__ */ React__default.default.createElement(
8372
8321
  "div",
8373
8322
  {
8374
- key: drilldown.label,
8375
- className: css.cx(styles.combinedFilterPill),
8376
- onClick: () => onClickAction(drilldown.onClick)
8323
+ ref: wrapperRef,
8324
+ className: css.cx(styles.comboboxWrapper, {
8325
+ [styles.comboboxFocusOutline]: !readOnly,
8326
+ [styles.collapsed]: shouldCollapse
8327
+ })
8377
8328
  },
8378
- drilldown.label
8379
- )) : /* @__PURE__ */ React__default.default.createElement("div", { className: styles.emptyMessage }, /* @__PURE__ */ React__default.default.createElement(i18n.Trans, { i18nKey: "grafana-scenes.components.drilldown-recommendations.recommended-empty" }, "No recommended values"))))));
8380
- return /* @__PURE__ */ React__default.default.createElement(React__default.default.Fragment, null, /* @__PURE__ */ React__default.default.createElement(
8381
- ui.IconButton,
8382
- {
8383
- name: "plus",
8384
- tooltip: i18n.t("grafana-scenes.components.drilldown-recommendations.tooltip", "Show recommendations"),
8385
- ref,
8386
- className: css.cx(isPopoverVisible && styles.iconActive),
8387
- onClick: (ev) => {
8388
- openPopover();
8389
- ev.stopPropagation();
8329
+ !readOnly && valueRecommendations && /* @__PURE__ */ React__default.default.createElement(valueRecommendations.Component, { model: valueRecommendations }),
8330
+ adhocFiltersToRender.map((filter, index) => /* @__PURE__ */ React__default.default.createElement(
8331
+ AdHocFilterPill,
8332
+ {
8333
+ key: `${filter.origin ? "origin-" : ""}${index}-${filter.key}`,
8334
+ filter,
8335
+ controller,
8336
+ readOnly: readOnly || filter.readOnly,
8337
+ focusOnWipInputRef: focusOnWipInputRef.current
8338
+ }
8339
+ )),
8340
+ shouldCollapse && adhocHiddenCount > 0 && /* @__PURE__ */ React__default.default.createElement(
8341
+ ui.Button,
8342
+ {
8343
+ className: styles.moreIndicator,
8344
+ fill: "text",
8345
+ size: "sm",
8346
+ "aria-label": i18n.t(
8347
+ "grafana-scenes.variables.adhoc-filters-combobox-renderer.show-more-filters",
8348
+ "Show {{count}} more filters",
8349
+ { count: adhocHiddenCount }
8350
+ ),
8351
+ onClick: (e) => {
8352
+ e.stopPropagation();
8353
+ handleExpand();
8354
+ setTimeout(() => {
8355
+ var _a2;
8356
+ return (_a2 = focusOnWipInputRef.current) == null ? void 0 : _a2.call(focusOnWipInputRef);
8357
+ });
8358
+ }
8359
+ },
8360
+ "+",
8361
+ adhocHiddenCount
8362
+ ),
8363
+ !readOnly && /* @__PURE__ */ React__default.default.createElement(AdHocFiltersAlwaysWipCombobox, { ref: focusOnWipInputRef, controller, onInputClick: handleExpand }),
8364
+ enableGroupBy && /* @__PURE__ */ React__default.default.createElement(React__default.default.Fragment, null, /* @__PURE__ */ React__default.default.createElement("div", { className: styles.sectionDivider }), /* @__PURE__ */ React__default.default.createElement("span", { className: styles.groupByLabel }, i18n.t("grafana-scenes.variables.adhoc-filters-combobox-renderer.group-by-label", "Group by:")), !readOnly && valueRecommendations && /* @__PURE__ */ React__default.default.createElement(AdHocGroupByRecommendationsRenderer, { model: valueRecommendations }), groupByFiltersToRender.map((filter, index) => /* @__PURE__ */ React__default.default.createElement(
8365
+ GroupByPill,
8366
+ {
8367
+ key: `groupby-${index}-${filter.key}`,
8368
+ filter,
8369
+ controller,
8370
+ readOnly: readOnly || filter.readOnly,
8371
+ focusOnWipInputRef: focusOnGroupByWipInputRef.current
8372
+ }
8373
+ )), shouldCollapse && groupByHiddenCount > 0 && /* @__PURE__ */ React__default.default.createElement(
8374
+ ui.Button,
8375
+ {
8376
+ className: styles.moreIndicator,
8377
+ fill: "text",
8378
+ size: "sm",
8379
+ "aria-label": i18n.t(
8380
+ "grafana-scenes.variables.adhoc-filters-combobox-renderer.show-more-group-by",
8381
+ "Show {{count}} more group by",
8382
+ { count: groupByHiddenCount }
8383
+ ),
8384
+ onClick: (e) => {
8385
+ e.stopPropagation();
8386
+ handleExpand();
8387
+ setTimeout(() => {
8388
+ var _a2;
8389
+ return (_a2 = focusOnGroupByWipInputRef.current) == null ? void 0 : _a2.call(focusOnGroupByWipInputRef);
8390
+ });
8391
+ }
8392
+ },
8393
+ "+",
8394
+ groupByHiddenCount
8395
+ ), !readOnly && /* @__PURE__ */ React__default.default.createElement(
8396
+ AdHocFiltersAlwaysWipCombobox,
8397
+ {
8398
+ ref: focusOnGroupByWipInputRef,
8399
+ controller,
8400
+ onInputClick: handleExpand,
8401
+ isGroupBy: true
8390
8402
  }
8391
- }
8392
- ), isPopoverVisible && ref.current && /* @__PURE__ */ React__default.default.createElement(
8393
- ui.Popover,
8394
- {
8395
- content,
8396
- onKeyDown: (event) => {
8397
- if (event.key === " ") {
8398
- event.stopPropagation();
8403
+ ), groupByRestorable && /* @__PURE__ */ React__default.default.createElement(
8404
+ ui.IconButton,
8405
+ {
8406
+ name: "history",
8407
+ size: "md",
8408
+ className: styles.controlButton,
8409
+ tooltip: i18n.t(
8410
+ "grafana-scenes.variables.adhoc-filters-combobox-renderer.restore-default-group-by",
8411
+ "Restore groupby set by this dashboard."
8412
+ ),
8413
+ onClick: () => {
8414
+ var _a2;
8415
+ return (_a2 = controller.restoreOriginalGroupBy) == null ? void 0 : _a2.call(controller);
8399
8416
  }
8417
+ }
8418
+ )),
8419
+ (showCollapseButton || showExpandButton || !readOnly) && /* @__PURE__ */ React__default.default.createElement("div", { className: styles.rightControls }, /* @__PURE__ */ React__default.default.createElement("div", { className: styles.sectionDivider }), showCollapseButton && /* @__PURE__ */ React__default.default.createElement(
8420
+ ui.Button,
8421
+ {
8422
+ className: styles.collapseButton,
8423
+ fill: "text",
8424
+ size: "sm",
8425
+ onClick: handleCollapseToggle,
8426
+ "aria-label": i18n.t(
8427
+ "grafana-scenes.variables.adhoc-filters-combobox-renderer.collapse-filters",
8428
+ "Collapse filters"
8429
+ ),
8430
+ "aria-expanded": !collapsed
8400
8431
  },
8401
- placement: "bottom-start",
8402
- referenceElement: ref.current,
8403
- show: true
8404
- }
8405
- ));
8406
- }
8432
+ i18n.t("grafana-scenes.variables.adhoc-filters-combobox-renderer.collapse", "Collapse"),
8433
+ /* @__PURE__ */ React__default.default.createElement(ui.Icon, { name: "angle-up", size: "md" })
8434
+ ), showExpandButton && /* @__PURE__ */ React__default.default.createElement(
8435
+ ui.IconButton,
8436
+ {
8437
+ name: "angle-down",
8438
+ size: "md",
8439
+ className: styles.dropdownIndicator,
8440
+ tooltip: i18n.t("grafana-scenes.variables.adhoc-filters-combobox-renderer.expand-filters", "Expand filters"),
8441
+ onClick: (e) => {
8442
+ e.stopPropagation();
8443
+ handleExpand();
8444
+ }
8445
+ }
8446
+ ), !readOnly && /* @__PURE__ */ React__default.default.createElement(
8447
+ ui.IconButton,
8448
+ {
8449
+ name: "times",
8450
+ size: "md",
8451
+ className: styles.controlButton,
8452
+ tooltip: i18n.t("grafana-scenes.variables.adhoc-filters-combobox-renderer.clear-all", "Clear all"),
8453
+ onClick: clearAll
8454
+ }
8455
+ ))
8456
+ );
8457
+ });
8407
8458
  const getStyles$b = (theme) => ({
8408
- menuContainer: css.css({
8459
+ comboboxWrapper: css.css({
8409
8460
  display: "flex",
8410
- flexDirection: "column",
8411
- background: theme.colors.background.elevated,
8412
- border: `1px solid ${theme.colors.border.weak}`,
8461
+ flexWrap: "wrap",
8462
+ alignItems: "center",
8463
+ columnGap: theme.spacing(1),
8464
+ rowGap: theme.spacing(0.5),
8465
+ minHeight: theme.spacing(4),
8466
+ backgroundColor: theme.components.input.background,
8467
+ border: `1px solid ${theme.colors.border.strong}`,
8413
8468
  borderRadius: theme.shape.radius.default,
8414
- boxShadow: theme.shadows.z3,
8415
- padding: theme.spacing(2)
8469
+ paddingInline: theme.spacing(1),
8470
+ paddingBlock: theme.spacing(0.5),
8471
+ flexGrow: 1,
8472
+ width: "100%"
8416
8473
  }),
8417
- combinedFilterPill: css.css({
8474
+ comboboxFocusOutline: css.css({
8475
+ "&:focus-within": {
8476
+ outline: "2px dotted transparent",
8477
+ outlineOffset: "2px",
8478
+ boxShadow: `0 0 0 2px ${theme.colors.background.canvas}, 0 0 0px 4px ${theme.colors.primary.main}`,
8479
+ transitionTimingFunction: `cubic-bezier(0.19, 1, 0.22, 1)`,
8480
+ transitionDuration: "0.2s",
8481
+ transitionProperty: "outline, outline-offset, box-shadow",
8482
+ zIndex: 2
8483
+ }
8484
+ }),
8485
+ collapsed: css.css({
8486
+ flexWrap: "nowrap",
8487
+ overflow: "hidden"
8488
+ }),
8489
+ rightControls: css.css({
8490
+ display: "flex",
8418
8491
  alignItems: "center",
8419
- background: theme.colors.action.selected,
8420
- borderRadius: theme.shape.radius.default,
8421
- border: `1px solid ${theme.colors.border.weak}`,
8422
- padding: theme.spacing(0.2, 1),
8492
+ marginLeft: "auto",
8493
+ flexShrink: 0,
8494
+ gap: theme.spacing(1.5)
8495
+ }),
8496
+ moreIndicator: css.css({
8423
8497
  color: theme.colors.text.primary,
8424
- overflow: "hidden",
8425
- whiteSpace: "nowrap",
8426
- minHeight: theme.spacing(2.75),
8427
- ...theme.typography.bodySmall,
8428
- fontWeight: theme.typography.fontWeightBold,
8429
- cursor: "pointer",
8498
+ background: theme.colors.action.selected,
8499
+ height: "auto",
8500
+ lineHeight: "normal",
8501
+ alignSelf: "stretch",
8430
8502
  "&:hover": {
8431
8503
  background: theme.colors.action.hover
8432
8504
  }
8433
8505
  }),
8434
- iconActive: css.css({
8435
- "&:before": {
8436
- backgroundColor: theme.colors.action.hover,
8437
- opacity: 1
8506
+ dropdownIndicator: css.css({
8507
+ color: theme.colors.text.secondary,
8508
+ flexShrink: 0
8509
+ }),
8510
+ collapseButton: css.css({
8511
+ color: theme.colors.text.secondary,
8512
+ padding: 0,
8513
+ height: "auto",
8514
+ lineHeight: "normal",
8515
+ "&:hover": {
8516
+ background: "transparent",
8517
+ color: theme.colors.text.primary
8438
8518
  }
8439
8519
  }),
8440
- emptyMessage: css.css({
8441
- padding: theme.spacing(0.5, 0),
8520
+ sectionDivider: css.css({
8521
+ width: "1px",
8522
+ alignSelf: "stretch",
8523
+ backgroundColor: theme.colors.border.weak,
8524
+ flexShrink: 0
8525
+ }),
8526
+ groupByLabel: css.css({
8527
+ ...theme.typography.bodySmall,
8528
+ fontWeight: theme.typography.fontWeightBold,
8529
+ color: theme.colors.text.primary,
8530
+ whiteSpace: "nowrap"
8531
+ }),
8532
+ controlButton: css.css({
8442
8533
  color: theme.colors.text.secondary,
8443
- ...theme.typography.bodySmall
8534
+ "&:hover": {
8535
+ color: theme.colors.text.primary
8536
+ }
8444
8537
  })
8445
8538
  });
8446
8539
 
8447
- const getRecentFiltersKey = (datasourceUid) => `grafana.filters.recent.${datasourceUid != null ? datasourceUid : "default"}`;
8448
- function getFilterIdentity(filter) {
8449
- return `${filter.key}|${filter.operator}|${filter.value}`;
8540
+ const reverseScopeFilterOperatorMap = Object.fromEntries(
8541
+ Object.entries(data.scopeFilterOperatorMap).map(([symbol, operator]) => [operator, symbol])
8542
+ );
8543
+ function isEqualityOrMultiOperator(value) {
8544
+ const operators = /* @__PURE__ */ new Set(["equals", "not-equals", "one-of", "not-one-of"]);
8545
+ return operators.has(value);
8450
8546
  }
8451
- function deduplicateFilters(filters) {
8452
- const filterMap = /* @__PURE__ */ new Map();
8453
- for (const filter of filters) {
8454
- const identity = getFilterIdentity(filter);
8455
- if (filterMap.has(identity)) {
8456
- filterMap.delete(identity);
8457
- }
8458
- filterMap.set(identity, filter);
8547
+ function isRegexOperator(value) {
8548
+ const operators = /* @__PURE__ */ new Set(["regex-match", "regex-not-match"]);
8549
+ return operators.has(value);
8550
+ }
8551
+ function getAdHocFiltersFromScopes(scopes) {
8552
+ const formattedFilters = /* @__PURE__ */ new Map();
8553
+ const duplicatedFilters = [];
8554
+ const allFilters = scopes.flatMap((scope) => scope.spec.filters);
8555
+ for (const filter of allFilters) {
8556
+ processFilter(formattedFilters, duplicatedFilters, filter);
8459
8557
  }
8460
- return Array.from(filterMap.values());
8558
+ return [...formattedFilters.values(), ...duplicatedFilters];
8461
8559
  }
8462
- class AdHocFiltersRecommendations extends SceneObjectBase {
8463
- constructor(state = {}) {
8464
- super(state);
8465
- this._activationHandler = () => {
8466
- const json = data.store.get(this._getStorageKey());
8467
- const storedFilters = json ? JSON.parse(json) : [];
8468
- if (storedFilters.length > 0) {
8469
- this._verifyRecentFiltersApplicability(storedFilters);
8470
- } else {
8471
- this.setState({ recentFilters: [] });
8472
- }
8473
- this._fetchRecommendedDrilldowns();
8474
- const scopesVariable = sceneGraph.lookupVariable(SCOPES_VARIABLE_NAME, this._adHocFilter);
8475
- let scopesSubscription;
8476
- let adHocSubscription;
8477
- if (scopesVariable instanceof ScopesVariable) {
8478
- this._subs.add(
8479
- scopesSubscription = scopesVariable.subscribeToState((newState, prevState) => {
8480
- if (newState.scopes !== prevState.scopes) {
8481
- const json2 = data.store.get(this._getStorageKey());
8482
- const storedFilters2 = json2 ? JSON.parse(json2) : [];
8483
- if (storedFilters2.length > 0) {
8484
- this._verifyRecentFiltersApplicability(storedFilters2);
8485
- }
8486
- this._fetchRecommendedDrilldowns();
8487
- }
8488
- })
8489
- );
8490
- }
8491
- this._subs.add(
8492
- adHocSubscription = this._adHocFilter.subscribeToState((newState, prevState) => {
8493
- if (newState.filters !== prevState.filters) {
8494
- const json2 = data.store.get(this._getStorageKey());
8495
- const storedFilters2 = json2 ? JSON.parse(json2) : [];
8496
- if (storedFilters2.length > 0) {
8497
- this._verifyRecentFiltersApplicability(storedFilters2);
8498
- }
8499
- this._fetchRecommendedDrilldowns();
8500
- }
8501
- })
8502
- );
8503
- return () => {
8504
- scopesSubscription == null ? void 0 : scopesSubscription.unsubscribe();
8505
- adHocSubscription == null ? void 0 : adHocSubscription.unsubscribe();
8506
- };
8507
- };
8508
- this.addActivationHandler(this._activationHandler);
8560
+ function processFilter(formattedFilters, duplicatedFilters, filter) {
8561
+ var _a, _b;
8562
+ if (!filter) {
8563
+ return;
8564
+ }
8565
+ const existingFilter = formattedFilters.get(filter.key);
8566
+ if (existingFilter && isEqualityValue(existingFilter.operator, filter.operator)) {
8567
+ mergeFilterValues(existingFilter, filter);
8568
+ } else if (existingFilter && isRegexValue(existingFilter.operator, filter.operator)) {
8569
+ existingFilter.value += `|${filter.value}`;
8570
+ existingFilter.values = [existingFilter.value];
8571
+ } else if (!existingFilter) {
8572
+ formattedFilters.set(filter.key, {
8573
+ key: filter.key,
8574
+ operator: reverseScopeFilterOperatorMap[filter.operator],
8575
+ value: filter.value,
8576
+ values: (_a = filter.values) != null ? _a : [filter.value],
8577
+ origin: "scope"
8578
+ });
8579
+ } else {
8580
+ duplicatedFilters.push({
8581
+ key: filter.key,
8582
+ operator: reverseScopeFilterOperatorMap[filter.operator],
8583
+ value: filter.value,
8584
+ values: (_b = filter.values) != null ? _b : [filter.value],
8585
+ origin: "scope"
8586
+ });
8509
8587
  }
8510
- get _adHocFilter() {
8511
- if (!(this.parent instanceof AdHocFiltersVariable)) {
8512
- throw new Error("AdHocFiltersRecommendations must be a child of AdHocFiltersVariable");
8588
+ }
8589
+ function mergeFilterValues(adHocFilter, filter) {
8590
+ var _a, _b, _c, _d;
8591
+ const values = (_a = filter.values) != null ? _a : [filter.value];
8592
+ for (const value of values) {
8593
+ if (!((_b = adHocFilter.values) == null ? void 0 : _b.includes(value))) {
8594
+ (_c = adHocFilter.values) == null ? void 0 : _c.push(value);
8513
8595
  }
8514
- return this.parent;
8515
8596
  }
8516
- get _scopedVars() {
8517
- return { __sceneObject: wrapInSafeSerializableSceneObject(this._adHocFilter) };
8597
+ if (((_d = adHocFilter.values) == null ? void 0 : _d.length) === 1) {
8598
+ return;
8518
8599
  }
8519
- _getStorageKey() {
8520
- var _a;
8521
- return getRecentFiltersKey((_a = this._adHocFilter.state.datasource) == null ? void 0 : _a.uid);
8600
+ if (filter.operator === "equals" && adHocFilter.operator === reverseScopeFilterOperatorMap["equals"]) {
8601
+ adHocFilter.operator = reverseScopeFilterOperatorMap["one-of"];
8602
+ } else if (filter.operator === "not-equals" && adHocFilter.operator === reverseScopeFilterOperatorMap["not-equals"]) {
8603
+ adHocFilter.operator = reverseScopeFilterOperatorMap["not-one-of"];
8522
8604
  }
8523
- async _fetchRecommendedDrilldowns() {
8524
- var _a;
8525
- const adhoc = this._adHocFilter;
8526
- const ds = await getDataSource(adhoc.state.datasource, this._scopedVars);
8527
- if (!ds || !ds.getRecommendedDrilldowns) {
8528
- this.setState({ datasourceSupportsRecommendations: false });
8529
- return;
8530
- }
8531
- this.setState({ datasourceSupportsRecommendations: true });
8532
- const queries = adhoc.state.useQueriesAsFilterForOptions ? getQueriesForVariables(adhoc) : void 0;
8533
- const timeRange = sceneGraph.getTimeRange(adhoc).state.value;
8534
- const scopes = sceneGraph.getScopes(adhoc);
8535
- const filters = [...(_a = adhoc.state.originFilters) != null ? _a : [], ...adhoc.state.filters].filter((f) => !isGroupByFilter(f));
8536
- const enrichedRequest = getEnrichedDataRequest(adhoc);
8537
- const dashboardUid = enrichedRequest == null ? void 0 : enrichedRequest.dashboardUID;
8538
- try {
8539
- const recommendedDrilldowns = await ds.getRecommendedDrilldowns({
8540
- timeRange,
8541
- dashboardUid,
8542
- queries: queries != null ? queries : [],
8543
- filters,
8544
- scopes
8545
- });
8546
- if (recommendedDrilldowns == null ? void 0 : recommendedDrilldowns.filters) {
8547
- this.setState({ recommendedFilters: recommendedDrilldowns.filters });
8548
- }
8549
- } catch (error) {
8550
- console.error("Failed to fetch recommended drilldowns:", error);
8551
- }
8605
+ }
8606
+ function isRegexValue(adHocFilterOperator, filterOperator) {
8607
+ const scopeConvertedOperator = data.scopeFilterOperatorMap[adHocFilterOperator];
8608
+ if (!isRegexOperator(scopeConvertedOperator) || !isRegexOperator(filterOperator)) {
8609
+ return false;
8552
8610
  }
8553
- async _verifyRecentFiltersApplicability(storedFilters) {
8554
- const adhoc = this._adHocFilter;
8555
- const queries = adhoc.state.useQueriesAsFilterForOptions ? getQueriesForVariables(adhoc) : void 0;
8556
- const response = await adhoc.getFiltersApplicabilityForQueries(storedFilters, queries != null ? queries : []);
8557
- if (!response) {
8558
- const deduped = deduplicateFilters(storedFilters);
8559
- this.setState({ recentFilters: deduped.slice(-3) });
8560
- return;
8611
+ return hasSameOperators(scopeConvertedOperator, filterOperator);
8612
+ }
8613
+ function isEqualityValue(adHocFilterOperator, filterOperator) {
8614
+ const scopeConvertedOperator = data.scopeFilterOperatorMap[adHocFilterOperator];
8615
+ if (!isEqualityOrMultiOperator(scopeConvertedOperator) || !isEqualityOrMultiOperator(filterOperator)) {
8616
+ return false;
8617
+ }
8618
+ return hasSameOperators(scopeConvertedOperator, filterOperator);
8619
+ }
8620
+ function hasSameOperators(scopeConvertedOperator, filterOperator) {
8621
+ if (scopeConvertedOperator.includes("not") && !filterOperator.includes("not") || !scopeConvertedOperator.includes("not") && filterOperator.includes("not")) {
8622
+ return false;
8623
+ }
8624
+ return true;
8625
+ }
8626
+
8627
+ function isInteractionTracker(s) {
8628
+ return "isInteractionTracker" in s;
8629
+ }
8630
+ class SceneInteractionTracker extends SceneObjectBase {
8631
+ constructor(state = {}, renderProfiler) {
8632
+ super(state);
8633
+ this.renderProfiler = renderProfiler;
8634
+ this.isInteractionTracker = true;
8635
+ if (renderProfiler) {
8636
+ this.renderProfiler = renderProfiler;
8637
+ this.renderProfiler.setInteractionCompleteHandler(state.onInteractionComplete);
8561
8638
  }
8562
- const applicabilityMap = /* @__PURE__ */ new Map();
8563
- response.forEach((item) => {
8564
- applicabilityMap.set(item.key, item.applicable !== false);
8565
- });
8566
- const applicableFilters = storedFilters.filter((f) => {
8567
- const isApplicable = applicabilityMap.get(f.key);
8568
- return isApplicable === void 0 || isApplicable === true;
8569
- });
8570
- const recentFilters = deduplicateFilters(applicableFilters).slice(-3);
8571
- this.setState({ recentFilters });
8572
8639
  }
8573
- /**
8574
- * Stores a recent filter in localStorage and updates state.
8575
- * Should be called by the parent variable when a filter is added/updated.
8576
- */
8577
- storeRecentFilter(filter) {
8578
- const key = this._getStorageKey();
8579
- const storedFilters = data.store.get(key);
8580
- const allRecentFilters = storedFilters ? JSON.parse(storedFilters) : [];
8581
- const updatedStoredFilters = deduplicateFilters([...allRecentFilters, filter]).slice(-10);
8582
- data.store.set(key, JSON.stringify(updatedStoredFilters));
8583
- const adhoc = this._adHocFilter;
8584
- const existingFilter = adhoc.state.filters.find((f) => f.key === filter.key && !Boolean(f.nonApplicable));
8585
- if (existingFilter && !Boolean(existingFilter.nonApplicable)) {
8586
- this.setState({ recentFilters: updatedStoredFilters.slice(-3) });
8640
+ startInteraction(name) {
8641
+ var _a;
8642
+ if (!this.state.enableInteractionTracking) {
8643
+ return;
8587
8644
  }
8645
+ (_a = this.renderProfiler) == null ? void 0 : _a.startInteraction(name);
8588
8646
  }
8589
- addFilterToParent(filter) {
8590
- this._adHocFilter.updateFilters([...this._adHocFilter.state.filters, filter]);
8647
+ stopInteraction() {
8648
+ var _a;
8649
+ (_a = this.renderProfiler) == null ? void 0 : _a.stopInteraction();
8591
8650
  }
8592
8651
  }
8593
- AdHocFiltersRecommendations.Component = AdHocFiltersRecommendationsRenderer;
8594
- function AdHocFiltersRecommendationsRenderer({ model }) {
8595
- const { recentFilters, recommendedFilters, datasourceSupportsRecommendations } = model.useState();
8596
- const { filters } = model._adHocFilter.useState();
8597
- const recentDrilldowns = recentFilters == null ? void 0 : recentFilters.map((filter) => ({
8598
- label: `${filter.key} ${filter.operator} ${filter.value}`,
8599
- onClick: () => {
8600
- const exists = filters.some((f) => f.key === filter.key && f.value === filter.value);
8601
- if (!exists) {
8602
- model.addFilterToParent(filter);
8603
- }
8604
- }
8605
- }));
8606
- const recommendedDrilldowns = recommendedFilters == null ? void 0 : recommendedFilters.map((filter) => ({
8607
- label: `${filter.key} ${filter.operator} ${filter.value}`,
8608
- onClick: () => {
8609
- const exists = filters.some((f) => f.key === filter.key && f.value === filter.value);
8610
- if (!exists) {
8611
- model.addFilterToParent(filter);
8652
+
8653
+ function getInteractionTracker(sceneObject) {
8654
+ let parent = sceneObject;
8655
+ while (parent) {
8656
+ if (parent.state.$behaviors) {
8657
+ for (const behavior of parent.state.$behaviors) {
8658
+ if (isInteractionTracker(behavior)) {
8659
+ return behavior;
8660
+ }
8612
8661
  }
8613
8662
  }
8614
- }));
8615
- return /* @__PURE__ */ React__default.default.createElement(
8616
- DrilldownRecommendations,
8617
- {
8618
- recentDrilldowns,
8619
- recommendedDrilldowns,
8620
- showRecommended: datasourceSupportsRecommendations
8621
- }
8622
- );
8663
+ parent = parent.parent;
8664
+ }
8665
+ return void 0;
8666
+ }
8667
+
8668
+ class AdHocFiltersVariableController {
8669
+ constructor(model) {
8670
+ this.model = model;
8671
+ }
8672
+ useState() {
8673
+ const state = this.model.useState();
8674
+ return {
8675
+ filters: state.filters,
8676
+ originFilters: state.originFilters,
8677
+ readOnly: state.readOnly,
8678
+ allowCustomValue: state.allowCustomValue,
8679
+ supportsMultiValueOperators: state.supportsMultiValueOperators,
8680
+ onAddCustomValue: state.onAddCustomValue,
8681
+ wip: state._wip,
8682
+ inputPlaceholder: state.inputPlaceholder,
8683
+ groupByInputPlaceholder: state.groupByInputPlaceholder,
8684
+ collapsible: state.collapsible,
8685
+ valueRecommendations: this.model.getRecommendations(),
8686
+ drilldownRecommendationsEnabled: state.drilldownRecommendationsEnabled,
8687
+ enableGroupBy: state.enableGroupBy,
8688
+ groupByRestorable: this.model.isGroupByRestorable()
8689
+ };
8690
+ }
8691
+ async getKeys(currentKey) {
8692
+ return this.model._getKeys(currentKey);
8693
+ }
8694
+ async getGroupByKeys(currentKey) {
8695
+ return this.model._getGroupByKeys(currentKey);
8696
+ }
8697
+ async getValuesFor(filter) {
8698
+ return this.model._getValuesFor(filter);
8699
+ }
8700
+ getOperators() {
8701
+ return this.model._getOperators();
8702
+ }
8703
+ updateFilter(filter, update) {
8704
+ this.model._updateFilter(filter, update);
8705
+ }
8706
+ updateFilters(filters, options) {
8707
+ this.model.updateFilters(filters, options);
8708
+ }
8709
+ updateToMatchAll(filter) {
8710
+ this.model.updateToMatchAll(filter);
8711
+ }
8712
+ removeFilter(filter) {
8713
+ this.model._removeFilter(filter);
8714
+ }
8715
+ removeLastFilter() {
8716
+ this.model._removeLastFilter();
8717
+ }
8718
+ handleComboboxBackspace(filter) {
8719
+ this.model._handleComboboxBackspace(filter);
8720
+ }
8721
+ addWip() {
8722
+ this.model._addWip();
8723
+ }
8724
+ addGroupByFilter(item) {
8725
+ this.model._addGroupByFilter(item);
8726
+ }
8727
+ restoreOriginalFilter(filter) {
8728
+ this.model.restoreOriginalFilter(filter);
8729
+ }
8730
+ restoreOriginalGroupBy() {
8731
+ this.model.restoreOriginalGroupBy();
8732
+ }
8733
+ clearAll() {
8734
+ this.model.clearAll();
8735
+ }
8736
+ startProfile(name) {
8737
+ const queryController = getQueryController(this.model);
8738
+ queryController == null ? void 0 : queryController.startProfile(name);
8739
+ }
8740
+ startInteraction(name) {
8741
+ const interactionTracker = getInteractionTracker(this.model);
8742
+ interactionTracker == null ? void 0 : interactionTracker.startInteraction(name);
8743
+ }
8744
+ stopInteraction() {
8745
+ const interactionTracker = getInteractionTracker(this.model);
8746
+ interactionTracker == null ? void 0 : interactionTracker.stopInteraction();
8747
+ }
8623
8748
  }
8624
8749
 
8625
8750
  const ORIGIN_FILTERS_KEY = "originFilters";
@@ -8858,7 +8983,7 @@ class AdHocFiltersVariable extends SceneObjectBase {
8858
8983
  * No-op when enableGroupBy is false.
8859
8984
  */
8860
8985
  _addGroupByFilter(item) {
8861
- var _a, _b;
8986
+ var _a, _b, _c;
8862
8987
  if (!this.state.enableGroupBy) {
8863
8988
  return;
8864
8989
  }
@@ -8871,6 +8996,7 @@ class AdHocFiltersVariable extends SceneObjectBase {
8871
8996
  value: "",
8872
8997
  condition: ""
8873
8998
  };
8999
+ (_c = this._recommendations) == null ? void 0 : _c.storeRecentGrouping(key);
8874
9000
  this.updateFilters([...this.state.filters, newFilter]);
8875
9001
  }
8876
9002
  restoreOriginalFilter(filter) {
@@ -9004,7 +9130,7 @@ class AdHocFiltersVariable extends SceneObjectBase {
9004
9130
  return this.state.filterExpression;
9005
9131
  }
9006
9132
  _updateFilter(filter, update) {
9007
- var _a, _b, _c, _d, _e;
9133
+ var _a, _b, _c, _d, _e, _f;
9008
9134
  const { originFilters, filters, _wip } = this.state;
9009
9135
  if ("value" in update && !("values" in update)) {
9010
9136
  update = { ...update, values: void 0 };
@@ -9074,10 +9200,12 @@ class AdHocFiltersVariable extends SceneObjectBase {
9074
9200
  return f === filter ? { ...f, ...update } : f;
9075
9201
  });
9076
9202
  this.setState({ filters: updatedFilters });
9077
- (_e = this._recommendations) == null ? void 0 : _e.storeRecentFilter({
9078
- ...filter,
9079
- ...update
9080
- });
9203
+ const merged = { ...filter, ...update };
9204
+ if (isGroupByFilter(merged)) {
9205
+ (_e = this._recommendations) == null ? void 0 : _e.storeRecentGrouping(merged.key);
9206
+ } else {
9207
+ (_f = this._recommendations) == null ? void 0 : _f.storeRecentFilter(merged);
9208
+ }
9081
9209
  }
9082
9210
  updateToMatchAll(filter) {
9083
9211
  var _a;