@grafana/scenes 6.50.0--canary.1312.20235582466.0 → 6.50.0--canary.1312.20242044820.0

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
@@ -3614,6 +3614,186 @@ function getInteractionTracker(sceneObject) {
3614
3614
  return void 0;
3615
3615
  }
3616
3616
 
3617
+ function DrilldownRecommendations({ recentDrilldowns, recommendedDrilldowns }) {
3618
+ const styles = ui.useStyles2(getStyles$k);
3619
+ const [isPopoverVisible, setPopoverVisible] = React.useState(false);
3620
+ const ref = React.useRef(null);
3621
+ const openPopover = () => {
3622
+ setPopoverVisible(true);
3623
+ };
3624
+ const onClickAction = (action) => {
3625
+ action();
3626
+ setPopoverVisible(false);
3627
+ };
3628
+ 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(
3629
+ "div",
3630
+ {
3631
+ key: drilldown.label,
3632
+ className: css.cx(styles.combinedFilterPill),
3633
+ onClick: () => onClickAction(drilldown.onClick)
3634
+ },
3635
+ drilldown.label
3636
+ )) : /* @__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")), /* @__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(
3637
+ "div",
3638
+ {
3639
+ key: drilldown.label,
3640
+ className: css.cx(styles.combinedFilterPill),
3641
+ onClick: () => onClickAction(drilldown.onClick)
3642
+ },
3643
+ drilldown.label
3644
+ )) : /* @__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")))));
3645
+ return /* @__PURE__ */ React__default.default.createElement(React__default.default.Fragment, null, /* @__PURE__ */ React__default.default.createElement(
3646
+ ui.IconButton,
3647
+ {
3648
+ name: "plus",
3649
+ tooltip: i18n.t("grafana-scenes.components.drilldown-recommendations.tooltip", "Show recommendations"),
3650
+ ref,
3651
+ className: css.cx(isPopoverVisible && styles.iconActive),
3652
+ onClick: (ev) => {
3653
+ openPopover();
3654
+ ev.stopPropagation();
3655
+ }
3656
+ }
3657
+ ), isPopoverVisible && ref.current && /* @__PURE__ */ React__default.default.createElement(
3658
+ ui.Popover,
3659
+ {
3660
+ content,
3661
+ onKeyDown: (event) => {
3662
+ if (event.key === " ") {
3663
+ event.stopPropagation();
3664
+ }
3665
+ },
3666
+ placement: "bottom-start",
3667
+ referenceElement: ref.current,
3668
+ show: true
3669
+ }
3670
+ ));
3671
+ }
3672
+ const getStyles$k = (theme) => ({
3673
+ menuContainer: css.css({
3674
+ display: "flex",
3675
+ flexDirection: "column",
3676
+ background: theme.colors.background.elevated,
3677
+ border: `1px solid ${theme.colors.border.weak}`,
3678
+ borderRadius: theme.shape.radius.default,
3679
+ boxShadow: theme.shadows.z3,
3680
+ padding: theme.spacing(2)
3681
+ }),
3682
+ combinedFilterPill: css.css({
3683
+ alignItems: "center",
3684
+ background: theme.colors.action.selected,
3685
+ borderRadius: theme.shape.radius.default,
3686
+ border: `1px solid ${theme.colors.border.weak}`,
3687
+ padding: theme.spacing(0.2, 1),
3688
+ color: theme.colors.text.primary,
3689
+ overflow: "hidden",
3690
+ whiteSpace: "nowrap",
3691
+ minHeight: theme.spacing(2.75),
3692
+ ...theme.typography.bodySmall,
3693
+ fontWeight: theme.typography.fontWeightBold,
3694
+ cursor: "pointer",
3695
+ "&:hover": {
3696
+ background: theme.colors.action.hover
3697
+ }
3698
+ }),
3699
+ iconActive: css.css({
3700
+ "&:before": {
3701
+ backgroundColor: theme.colors.action.hover,
3702
+ opacity: 1
3703
+ }
3704
+ }),
3705
+ emptyMessage: css.css({
3706
+ padding: theme.spacing(0.5, 0),
3707
+ color: theme.colors.text.secondary,
3708
+ ...theme.typography.bodySmall
3709
+ })
3710
+ });
3711
+
3712
+ class ScopesVariable extends SceneObjectBase {
3713
+ constructor(state) {
3714
+ super({
3715
+ skipUrlSync: true,
3716
+ loading: true,
3717
+ scopes: [],
3718
+ ...state,
3719
+ type: "system",
3720
+ name: SCOPES_VARIABLE_NAME,
3721
+ hide: schema.VariableHide.hideVariable
3722
+ });
3723
+ this._renderBeforeActivation = true;
3724
+ // Special options that enables variables to be hidden but still render to access react contexts
3725
+ this.UNSAFE_renderAsHidden = true;
3726
+ }
3727
+ /**
3728
+ * Temporary simple implementation to stringify the scopes.
3729
+ */
3730
+ getValue() {
3731
+ var _a;
3732
+ const scopes = (_a = this.state.scopes) != null ? _a : [];
3733
+ return new ScopesVariableFormatter(scopes.map((scope) => scope.metadata.name));
3734
+ }
3735
+ getScopes() {
3736
+ return this.state.scopes;
3737
+ }
3738
+ /**
3739
+ * This method is used to keep the context up to date with the scopes context received from React
3740
+ * 1) Subscribes to ScopesContext state changes and synchronizes it with the variable state
3741
+ * 2) Handles enable / disabling of scopes based on variable enable option.
3742
+ */
3743
+ setContext(context) {
3744
+ if (!context) {
3745
+ return;
3746
+ }
3747
+ this._context = context;
3748
+ const oldState = context.state;
3749
+ if (this.state.enable != null) {
3750
+ context.setEnabled(this.state.enable);
3751
+ }
3752
+ const sub = context.stateObservable.subscribe((state) => {
3753
+ this.updateStateFromContext(state);
3754
+ });
3755
+ return () => {
3756
+ sub.unsubscribe();
3757
+ if (this.state.enable != null) {
3758
+ context.setEnabled(oldState.enabled);
3759
+ }
3760
+ };
3761
+ }
3762
+ updateStateFromContext(state) {
3763
+ const loading = state.value.length === 0 ? false : state.loading;
3764
+ const oldScopes = this.state.scopes.map((scope) => scope.metadata.name);
3765
+ const newScopes = state.value.map((scope) => scope.metadata.name);
3766
+ const scopesHaveChanged = !lodash.isEqual(oldScopes, newScopes);
3767
+ if (!loading && (scopesHaveChanged || newScopes.length === 0)) {
3768
+ const queryController = getQueryController(this);
3769
+ queryController == null ? void 0 : queryController.startProfile(SCOPES_CHANGED_INTERACTION);
3770
+ this.setState({ scopes: state.value, loading });
3771
+ this.publishEvent(new SceneVariableValueChangedEvent(this), true);
3772
+ } else {
3773
+ this.setState({ loading });
3774
+ }
3775
+ }
3776
+ }
3777
+ ScopesVariable.Component = ScopesVariableRenderer;
3778
+ function ScopesVariableRenderer({ model }) {
3779
+ const context = React.useContext(runtime.ScopesContext);
3780
+ React.useEffect(() => {
3781
+ return model.setContext(context);
3782
+ }, [context, model]);
3783
+ return null;
3784
+ }
3785
+ class ScopesVariableFormatter {
3786
+ constructor(_value) {
3787
+ this._value = _value;
3788
+ }
3789
+ formatter(formatNameOrFn) {
3790
+ if (formatNameOrFn === schema.VariableFormatID.QueryParam) {
3791
+ return this._value.map((scope) => `scope=${encodeURIComponent(scope)}`).join("&");
3792
+ }
3793
+ return this._value.join(", ");
3794
+ }
3795
+ }
3796
+
3617
3797
  let originalGetAdhocFilters = void 0;
3618
3798
  const allActiveFilterSets = /* @__PURE__ */ new Set();
3619
3799
  function patchGetAdhocFilters(filterVar) {
@@ -3673,7 +3853,7 @@ function LoadingIndicator(props) {
3673
3853
  }
3674
3854
 
3675
3855
  function ControlsLabel(props) {
3676
- const styles = ui.useStyles2(getStyles$k);
3856
+ const styles = ui.useStyles2(getStyles$j);
3677
3857
  const theme = ui.useTheme2();
3678
3858
  const isVertical = props.layout === "vertical";
3679
3859
  const loadingIndicator = Boolean(props.isLoading) ? /* @__PURE__ */ React__default.default.createElement(
@@ -3720,7 +3900,7 @@ function ControlsLabel(props) {
3720
3900
  }
3721
3901
  return labelElement;
3722
3902
  }
3723
- const getStyles$k = (theme) => ({
3903
+ const getStyles$j = (theme) => ({
3724
3904
  horizontalLabel: css.css({
3725
3905
  background: theme.isDark ? theme.colors.background.primary : theme.colors.background.secondary,
3726
3906
  display: `flex`,
@@ -3773,7 +3953,7 @@ function keyLabelToOption(key, label) {
3773
3953
  const filterNoOp$1 = () => true;
3774
3954
  function AdHocFilterRenderer({ filter, model }) {
3775
3955
  var _a, _b, _c, _d, _e;
3776
- const styles = ui.useStyles2(getStyles$j);
3956
+ const styles = ui.useStyles2(getStyles$i);
3777
3957
  const [keys, setKeys] = React.useState([]);
3778
3958
  const [values, setValues] = React.useState([]);
3779
3959
  const [isKeysLoading, setIsKeysLoading] = React.useState(false);
@@ -4000,7 +4180,7 @@ function AdHocFilterRenderer({ filter, model }) {
4000
4180
  }
4001
4181
  ));
4002
4182
  }
4003
- const getStyles$j = (theme) => ({
4183
+ const getStyles$i = (theme) => ({
4004
4184
  field: css.css({
4005
4185
  marginBottom: 0
4006
4186
  }),
@@ -4073,7 +4253,7 @@ const getStyles$j = (theme) => ({
4073
4253
 
4074
4254
  function AdHocFilterBuilder({ model, addFilterButtonText }) {
4075
4255
  const { _wip } = model.useState();
4076
- const styles = ui.useStyles2(getStyles$i);
4256
+ const styles = ui.useStyles2(getStyles$h);
4077
4257
  if (!_wip) {
4078
4258
  return /* @__PURE__ */ React__default.default.createElement(
4079
4259
  ui.Button,
@@ -4091,7 +4271,7 @@ function AdHocFilterBuilder({ model, addFilterButtonText }) {
4091
4271
  }
4092
4272
  return /* @__PURE__ */ React__default.default.createElement(AdHocFilterRenderer, { filter: _wip, model });
4093
4273
  }
4094
- const getStyles$i = (theme) => ({
4274
+ const getStyles$h = (theme) => ({
4095
4275
  addButton: css.css({
4096
4276
  "&:first-child": {
4097
4277
  borderBottomLeftRadius: 0,
@@ -4216,7 +4396,7 @@ function isFilter(filter) {
4216
4396
 
4217
4397
  const DropdownItem = React.forwardRef(
4218
4398
  function DropdownItem2({ children, active, addGroupBottomBorder, isMultiValueEdit, checked, ...rest }, ref) {
4219
- const styles = ui.useStyles2(getStyles$h);
4399
+ const styles = ui.useStyles2(getStyles$g);
4220
4400
  const id = React.useId();
4221
4401
  return /* @__PURE__ */ React__default.default.createElement(
4222
4402
  "div",
@@ -4232,7 +4412,7 @@ const DropdownItem = React.forwardRef(
4232
4412
  );
4233
4413
  }
4234
4414
  );
4235
- const getStyles$h = (theme) => ({
4415
+ const getStyles$g = (theme) => ({
4236
4416
  option: css.css({
4237
4417
  label: "grafana-select-option",
4238
4418
  top: 0,
@@ -4302,7 +4482,7 @@ const MultiValueApplyButton = ({
4302
4482
  maxOptionWidth,
4303
4483
  menuHeight
4304
4484
  }) => {
4305
- const styles = ui.useStyles2(getStyles$h);
4485
+ const styles = ui.useStyles2(getStyles$g);
4306
4486
  const floatingElementRect = floatingElement == null ? void 0 : floatingElement.getBoundingClientRect();
4307
4487
  return /* @__PURE__ */ React__default.default.createElement(
4308
4488
  "div",
@@ -4523,7 +4703,7 @@ const MultiValuePill = ({
4523
4703
  handleEditMultiValuePill
4524
4704
  }) => {
4525
4705
  var _a, _b;
4526
- const styles = ui.useStyles2(getStyles$g);
4706
+ const styles = ui.useStyles2(getStyles$f);
4527
4707
  const editMultiValuePill = React.useCallback(
4528
4708
  (e) => {
4529
4709
  e.stopPropagation();
@@ -4587,7 +4767,7 @@ const MultiValuePill = ({
4587
4767
  )
4588
4768
  );
4589
4769
  };
4590
- const getStyles$g = (theme) => ({
4770
+ const getStyles$f = (theme) => ({
4591
4771
  basePill: css.css({
4592
4772
  display: "flex",
4593
4773
  alignItems: "center",
@@ -4633,7 +4813,7 @@ const AdHocCombobox = React.forwardRef(function AdHocCombobox2({
4633
4813
  const [activeIndex, setActiveIndex] = React.useState(null);
4634
4814
  const [filterInputType, setInputType] = React.useState(!isAlwaysWip ? "value" : "key");
4635
4815
  const [preventFiltering, setPreventFiltering] = React.useState(!isAlwaysWip && filterInputType === "value");
4636
- const styles = ui.useStyles2(getStyles$f);
4816
+ const styles = ui.useStyles2(getStyles$e);
4637
4817
  const [filterMultiValues, setFilterMultiValues] = React.useState([]);
4638
4818
  const [_, setForceRefresh] = React.useState({});
4639
4819
  const { allowCustomValue = true, onAddCustomValue, filters, inputPlaceholder } = controller.useState();
@@ -5239,7 +5419,7 @@ const AdHocCombobox = React.forwardRef(function AdHocCombobox2({
5239
5419
  }
5240
5420
  ) : null))));
5241
5421
  });
5242
- const getStyles$f = (theme) => ({
5422
+ const getStyles$e = (theme) => ({
5243
5423
  comboboxWrapper: css.css({
5244
5424
  display: "flex",
5245
5425
  flexWrap: "wrap"
@@ -5313,7 +5493,7 @@ const getStyles$f = (theme) => ({
5313
5493
  const LABEL_MAX_VISIBLE_LENGTH = 20;
5314
5494
  function AdHocFilterPill({ filter, controller, readOnly, focusOnWipInputRef }) {
5315
5495
  var _a, _b, _c, _d;
5316
- const styles = ui.useStyles2(getStyles$e);
5496
+ const styles = ui.useStyles2(getStyles$d);
5317
5497
  const [viewMode, setViewMode] = React.useState(true);
5318
5498
  const [shouldFocusOnPillWrapper, setShouldFocusOnPillWrapper] = React.useState(false);
5319
5499
  const pillWrapperRef = React.useRef(null);
@@ -5491,7 +5671,7 @@ function AdHocFilterPill({ filter, controller, readOnly, focusOnWipInputRef }) {
5491
5671
  }
5492
5672
  );
5493
5673
  }
5494
- const getStyles$e = (theme) => ({
5674
+ const getStyles$d = (theme) => ({
5495
5675
  combinedFilterPill: css.css({
5496
5676
  display: "flex",
5497
5677
  alignItems: "center",
@@ -5573,134 +5753,21 @@ const AdHocFiltersAlwaysWipCombobox = React.forwardRef(function AdHocFiltersAlwa
5573
5753
  return /* @__PURE__ */ React__default.default.createElement(AdHocCombobox, { controller, filter: wip, isAlwaysWip: true, ref: parentRef });
5574
5754
  });
5575
5755
 
5576
- function DrilldownRecommendations({ recentDrilldowns, recommendedDrilldowns }) {
5577
- const styles = ui.useStyles2(getStyles$d);
5578
- const [isPopoverVisible, setPopoverVisible] = React.useState(false);
5579
- const ref = React.useRef(null);
5580
- const openPopover = () => {
5581
- setPopoverVisible(true);
5582
- };
5583
- const onClickAction = (action) => {
5584
- action();
5585
- setPopoverVisible(false);
5586
- };
5587
- 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(
5588
- "div",
5589
- {
5590
- key: drilldown.label,
5591
- className: css.cx(styles.combinedFilterPill),
5592
- onClick: () => onClickAction(drilldown.onClick)
5593
- },
5594
- drilldown.label
5595
- )) : /* @__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")), /* @__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(
5756
+ const AdHocFiltersComboboxRenderer = React.memo(function AdHocFiltersComboboxRenderer2({ controller }) {
5757
+ const { originFilters, filters, readOnly, valueRecommendations, drilldownRecommendationsEnabled } = controller.useState();
5758
+ const styles = ui.useStyles2(getStyles$c);
5759
+ const focusOnWipInputRef = React.useRef();
5760
+ return /* @__PURE__ */ React__default.default.createElement(
5596
5761
  "div",
5597
5762
  {
5598
- key: drilldown.label,
5599
- className: css.cx(styles.combinedFilterPill),
5600
- onClick: () => onClickAction(drilldown.onClick)
5601
- },
5602
- drilldown.label
5603
- )) : /* @__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")))));
5604
- return /* @__PURE__ */ React__default.default.createElement(React__default.default.Fragment, null, /* @__PURE__ */ React__default.default.createElement(
5605
- ui.IconButton,
5606
- {
5607
- name: "plus",
5608
- tooltip: i18n.t("grafana-scenes.components.drilldown-recommendations.tooltip", "Show recommendations"),
5609
- ref,
5610
- className: css.cx(isPopoverVisible && styles.iconActive),
5611
- onClick: (ev) => {
5612
- openPopover();
5613
- ev.stopPropagation();
5614
- }
5615
- }
5616
- ), isPopoverVisible && ref.current && /* @__PURE__ */ React__default.default.createElement(
5617
- ui.Popover,
5618
- {
5619
- content,
5620
- onKeyDown: (event) => {
5621
- if (event.key === " ") {
5622
- event.stopPropagation();
5623
- }
5624
- },
5625
- placement: "bottom-start",
5626
- referenceElement: ref.current,
5627
- show: true
5628
- }
5629
- ));
5630
- }
5631
- const getStyles$d = (theme) => ({
5632
- menuContainer: css.css({
5633
- display: "flex",
5634
- flexDirection: "column",
5635
- background: theme.colors.background.elevated,
5636
- border: `1px solid ${theme.colors.border.weak}`,
5637
- borderRadius: theme.shape.radius.default,
5638
- boxShadow: theme.shadows.z3,
5639
- padding: theme.spacing(2)
5640
- }),
5641
- combinedFilterPill: css.css({
5642
- alignItems: "center",
5643
- background: theme.colors.action.selected,
5644
- borderRadius: theme.shape.radius.default,
5645
- border: `1px solid ${theme.colors.border.weak}`,
5646
- padding: theme.spacing(0.2, 1),
5647
- color: theme.colors.text.primary,
5648
- overflow: "hidden",
5649
- whiteSpace: "nowrap",
5650
- minHeight: theme.spacing(2.75),
5651
- ...theme.typography.bodySmall,
5652
- fontWeight: theme.typography.fontWeightBold,
5653
- cursor: "pointer",
5654
- "&:hover": {
5655
- background: theme.colors.action.hover
5656
- }
5657
- }),
5658
- iconActive: css.css({
5659
- "&:before": {
5660
- backgroundColor: theme.colors.action.hover,
5661
- opacity: 1
5662
- }
5663
- }),
5664
- emptyMessage: css.css({
5665
- padding: theme.spacing(0.5, 0),
5666
- color: theme.colors.text.secondary,
5667
- ...theme.typography.bodySmall
5668
- })
5669
- });
5670
-
5671
- const AdHocFiltersComboboxRenderer = React.memo(function AdHocFiltersComboboxRenderer2({ controller }) {
5672
- const { originFilters, filters, readOnly, recentFilters, recommendedFilters, drilldownRecommendationsEnabled } = controller.useState();
5673
- const styles = ui.useStyles2(getStyles$c);
5674
- let drilldownRecommendationComponent = null;
5675
- if (drilldownRecommendationsEnabled) {
5676
- const recentDrilldowns = recentFilters == null ? void 0 : recentFilters.map((filter) => ({
5677
- label: `${filter.key} ${filter.operator} ${filter.value}`,
5678
- onClick: () => {
5679
- var _a;
5680
- (_a = controller.updateFilters) == null ? void 0 : _a.call(controller, [...filters, filter]);
5681
- }
5682
- }));
5683
- const recommendedDrilldowns = recommendedFilters == null ? void 0 : recommendedFilters.map((filter) => ({
5684
- label: `${filter.key} ${filter.operator} ${filter.value}`,
5685
- onClick: () => {
5686
- var _a;
5687
- (_a = controller.updateFilters) == null ? void 0 : _a.call(controller, [...filters, filter]);
5688
- }
5689
- }));
5690
- drilldownRecommendationComponent = /* @__PURE__ */ React__default.default.createElement(DrilldownRecommendations, { recentDrilldowns, recommendedDrilldowns });
5691
- }
5692
- const focusOnWipInputRef = React.useRef();
5693
- return /* @__PURE__ */ React__default.default.createElement(
5694
- "div",
5695
- {
5696
- className: css.cx(styles.comboboxWrapper, { [styles.comboboxFocusOutline]: !readOnly }),
5697
- onClick: () => {
5698
- var _a;
5699
- (_a = focusOnWipInputRef.current) == null ? void 0 : _a.call(focusOnWipInputRef);
5763
+ className: css.cx(styles.comboboxWrapper, { [styles.comboboxFocusOutline]: !readOnly }),
5764
+ onClick: () => {
5765
+ var _a;
5766
+ (_a = focusOnWipInputRef.current) == null ? void 0 : _a.call(focusOnWipInputRef);
5700
5767
  }
5701
5768
  },
5702
5769
  /* @__PURE__ */ React__default.default.createElement(ui.Icon, { name: "filter", className: styles.filterIcon, size: "lg" }),
5703
- drilldownRecommendationComponent,
5770
+ drilldownRecommendationsEnabled && valueRecommendations && /* @__PURE__ */ React__default.default.createElement(valueRecommendations.Component, { model: valueRecommendations }),
5704
5771
  originFilters == null ? void 0 : originFilters.map(
5705
5772
  (filter, index) => filter.origin ? /* @__PURE__ */ React__default.default.createElement(
5706
5773
  AdHocFilterPill,
@@ -5858,8 +5925,7 @@ class AdHocFiltersVariableController {
5858
5925
  supportsMultiValueOperators: state.supportsMultiValueOperators,
5859
5926
  onAddCustomValue: state.onAddCustomValue,
5860
5927
  wip: state._wip,
5861
- recentFilters: state._recentFilters,
5862
- recommendedFilters: state._recommendedFilters,
5928
+ valueRecommendations: state._valueRecommendations,
5863
5929
  drilldownRecommendationsEnabled: state.drilldownRecommendationsEnabled
5864
5930
  };
5865
5931
  }
@@ -5910,6 +5976,163 @@ class AdHocFiltersVariableController {
5910
5976
  }
5911
5977
  }
5912
5978
 
5979
+ class AdHocFiltersRecommendations extends SceneObjectBase {
5980
+ constructor(parent) {
5981
+ super({});
5982
+ this._scopedVars = { __sceneObject: wrapInSafeSerializableSceneObject(this) };
5983
+ this._activationHandler = () => {
5984
+ const json = data.store.get(this._getStorageKey());
5985
+ const storedFilters = json ? JSON.parse(json) : [];
5986
+ if (storedFilters.length > 0) {
5987
+ this._verifyRecentFiltersApplicability(storedFilters);
5988
+ } else {
5989
+ this.setState({ recentFilters: [] });
5990
+ }
5991
+ this._fetchRecommendedDrilldowns();
5992
+ const scopesVariable = sceneGraph.lookupVariable(SCOPES_VARIABLE_NAME, this);
5993
+ let scopesSubscription;
5994
+ if (scopesVariable instanceof ScopesVariable) {
5995
+ scopesSubscription = scopesVariable.subscribeToState((newState, prevState) => {
5996
+ if (newState.scopes !== prevState.scopes) {
5997
+ const json2 = data.store.get(this._getStorageKey());
5998
+ const storedFilters2 = json2 ? JSON.parse(json2) : [];
5999
+ if (storedFilters2.length > 0) {
6000
+ this._verifyRecentFiltersApplicability(storedFilters2);
6001
+ }
6002
+ }
6003
+ });
6004
+ }
6005
+ return () => {
6006
+ scopesSubscription == null ? void 0 : scopesSubscription.unsubscribe();
6007
+ };
6008
+ };
6009
+ this._parentVariable = parent;
6010
+ this.addActivationHandler(this._activationHandler);
6011
+ }
6012
+ /**
6013
+ * Get the parent variable
6014
+ */
6015
+ get parent() {
6016
+ return this._parentVariable;
6017
+ }
6018
+ _getStorageKey() {
6019
+ var _a;
6020
+ return getRecentFiltersKey((_a = this._parentVariable.state.datasource) == null ? void 0 : _a.uid);
6021
+ }
6022
+ async _fetchRecommendedDrilldowns() {
6023
+ var _a;
6024
+ const parent = this._parentVariable;
6025
+ const ds = await getDataSource(parent.state.datasource, this._scopedVars);
6026
+ if (!ds || !ds.getRecommendedDrilldowns) {
6027
+ return;
6028
+ }
6029
+ const queries = parent.state.useQueriesAsFilterForOptions ? getQueriesForVariables(parent) : void 0;
6030
+ const timeRange = sceneGraph.getTimeRange(this).state.value;
6031
+ const scopes = sceneGraph.getScopes(this);
6032
+ const filters = [...(_a = parent.state.originFilters) != null ? _a : [], ...parent.state.filters];
6033
+ const enrichedRequest = getEnrichedDataRequest(this);
6034
+ const dashboardUid = enrichedRequest == null ? void 0 : enrichedRequest.dashboardUID;
6035
+ try {
6036
+ const recommendedDrilldowns = await ds.getRecommendedDrilldowns({
6037
+ timeRange,
6038
+ dashboardUid,
6039
+ queries: queries != null ? queries : [],
6040
+ filters,
6041
+ scopes,
6042
+ userId: runtime.config.bootData.user.id
6043
+ });
6044
+ if (recommendedDrilldowns == null ? void 0 : recommendedDrilldowns.filters) {
6045
+ this.setState({ recommendedFilters: recommendedDrilldowns.filters });
6046
+ }
6047
+ } catch (error) {
6048
+ console.error("Failed to fetch recommended drilldowns:", error);
6049
+ }
6050
+ }
6051
+ async _verifyRecentFiltersApplicability(storedFilters) {
6052
+ const parent = this._parentVariable;
6053
+ const queries = parent.state.useQueriesAsFilterForOptions ? getQueriesForVariables(parent) : void 0;
6054
+ const response = await this._getFiltersApplicabilityForQueries(storedFilters, queries != null ? queries : []);
6055
+ if (!response) {
6056
+ this.setState({ recentFilters: storedFilters.slice(-3) });
6057
+ return;
6058
+ }
6059
+ const applicabilityMap = /* @__PURE__ */ new Map();
6060
+ response.forEach((item) => {
6061
+ applicabilityMap.set(item.key, item.applicable !== false);
6062
+ });
6063
+ const applicableFilters = storedFilters.filter((f) => {
6064
+ const isApplicable = applicabilityMap.get(f.key);
6065
+ return isApplicable === void 0 || isApplicable === true;
6066
+ }).slice(-3);
6067
+ this.setState({ recentFilters: applicableFilters });
6068
+ }
6069
+ async _getFiltersApplicabilityForQueries(filters, queries) {
6070
+ const parent = this._parentVariable;
6071
+ const ds = await getDataSource(parent.state.datasource, this._scopedVars);
6072
+ if (!ds || !ds.getDrilldownsApplicability) {
6073
+ return;
6074
+ }
6075
+ const timeRange = sceneGraph.getTimeRange(this).state.value;
6076
+ return await ds.getDrilldownsApplicability({
6077
+ filters,
6078
+ queries,
6079
+ timeRange,
6080
+ scopes: sceneGraph.getScopes(this),
6081
+ ...getEnrichedFiltersRequest(this)
6082
+ });
6083
+ }
6084
+ /**
6085
+ * Stores a recent filter in localStorage and updates state.
6086
+ * Should be called by the parent variable when a filter is added/updated.
6087
+ */
6088
+ storeRecentFilter(filter) {
6089
+ const key = this._getStorageKey();
6090
+ const storedFilters = data.store.get(key);
6091
+ const allRecentFilters = storedFilters ? JSON.parse(storedFilters) : [];
6092
+ const updatedStoredFilters = [...allRecentFilters, filter].slice(-10);
6093
+ data.store.set(key, JSON.stringify(updatedStoredFilters));
6094
+ const parent = this._parentVariable;
6095
+ const existingFilter = parent.state.filters.find((f) => f.key === filter.key && !Boolean(f.nonApplicable));
6096
+ if (existingFilter && !Boolean(existingFilter.nonApplicable)) {
6097
+ this.setState({ recentFilters: updatedStoredFilters.slice(-3) });
6098
+ }
6099
+ }
6100
+ /**
6101
+ * Get the current filters from the parent variable
6102
+ */
6103
+ getParentFilters() {
6104
+ return this._parentVariable.state.filters;
6105
+ }
6106
+ /**
6107
+ * Add a filter to the parent variable
6108
+ */
6109
+ addFilterToParent(filter) {
6110
+ const parent = this._parentVariable;
6111
+ parent.updateFilters([...parent.state.filters, filter]);
6112
+ }
6113
+ }
6114
+ AdHocFiltersRecommendations.Component = AdHocFiltersRecommendationsRenderer;
6115
+ function AdHocFiltersRecommendationsRenderer({ model }) {
6116
+ const { recentFilters, recommendedFilters } = model.useState();
6117
+ const { filters } = model.parent.useState();
6118
+ const recentDrilldowns = recentFilters == null ? void 0 : recentFilters.map((filter) => ({
6119
+ label: `${filter.key} ${filter.operator} ${filter.value}`,
6120
+ onClick: () => {
6121
+ model.addFilterToParent(filter);
6122
+ }
6123
+ }));
6124
+ const recommendedDrilldowns = recommendedFilters == null ? void 0 : recommendedFilters.map((filter) => ({
6125
+ label: `${filter.key} ${filter.operator} ${filter.value}`,
6126
+ onClick: () => {
6127
+ const exists = filters.some((f) => f.key === filter.key && f.value === filter.value);
6128
+ if (!exists) {
6129
+ model.addFilterToParent(filter);
6130
+ }
6131
+ }
6132
+ }));
6133
+ return /* @__PURE__ */ React__default.default.createElement(DrilldownRecommendations, { recentDrilldowns, recommendedDrilldowns });
6134
+ }
6135
+
5913
6136
  const ORIGIN_FILTERS_KEY = "originFilters";
5914
6137
  const getRecentFiltersKey = (datasourceUid) => `grafana.filters.recent.${datasourceUid != null ? datasourceUid : "default"}`;
5915
6138
  const OPERATORS = [
@@ -5985,21 +6208,15 @@ class AdHocFiltersVariable extends SceneObjectBase {
5985
6208
  this._urlSync = new AdHocFiltersVariableUrlSyncHandler(this);
5986
6209
  this._debouncedVerifyApplicability = lodash.debounce(this._verifyApplicability, 100);
5987
6210
  this._activationHandler = () => {
5988
- var _a;
5989
6211
  this._debouncedVerifyApplicability();
5990
- if (this.state.drilldownRecommendationsEnabled) {
5991
- const json = data.store.get(getRecentFiltersKey((_a = this.state.datasource) == null ? void 0 : _a.uid));
5992
- const storedFilters = json ? JSON.parse(json) : [];
5993
- if (storedFilters.length > 0) {
5994
- this._verifyRecentFiltersApplicability(storedFilters);
5995
- } else {
5996
- this.setState({ _recentFilters: [] });
5997
- }
5998
- this._fetchRecommendedDrilldowns();
6212
+ if (this.state.drilldownRecommendationsEnabled && !this.state._valueRecommendations) {
6213
+ const valueRecommendations = new AdHocFiltersRecommendations(this);
6214
+ this.setState({ _valueRecommendations: valueRecommendations });
6215
+ valueRecommendations.activate();
5999
6216
  }
6000
6217
  return () => {
6001
- var _a2;
6002
- (_a2 = this.state.originFilters) == null ? void 0 : _a2.forEach((filter) => {
6218
+ var _a;
6219
+ (_a = this.state.originFilters) == null ? void 0 : _a.forEach((filter) => {
6003
6220
  if (filter.restorable) {
6004
6221
  this.restoreOriginalFilter(filter);
6005
6222
  }
@@ -6019,34 +6236,6 @@ class AdHocFiltersVariable extends SceneObjectBase {
6019
6236
  });
6020
6237
  this.addActivationHandler(this._activationHandler);
6021
6238
  }
6022
- async _fetchRecommendedDrilldowns() {
6023
- var _a;
6024
- const ds = await this._dataSourceSrv.get(this.state.datasource, this._scopedVars);
6025
- if (!ds || !ds.getRecommendedDrilldowns) {
6026
- return;
6027
- }
6028
- const queries = this.state.useQueriesAsFilterForOptions ? getQueriesForVariables(this) : void 0;
6029
- const timeRange = sceneGraph.getTimeRange(this).state.value;
6030
- const scopes = sceneGraph.getScopes(this);
6031
- const filters = [...(_a = this.state.originFilters) != null ? _a : [], ...this.state.filters];
6032
- const enrichedRequest = getEnrichedDataRequest(this);
6033
- const dashboardUid = enrichedRequest == null ? void 0 : enrichedRequest.dashboardUID;
6034
- try {
6035
- const recommendedDrilldowns = await ds.getRecommendedDrilldowns({
6036
- timeRange,
6037
- dashboardUid,
6038
- queries: queries != null ? queries : [],
6039
- filters,
6040
- scopes,
6041
- userId: runtime.config.bootData.user.id
6042
- });
6043
- if (recommendedDrilldowns == null ? void 0 : recommendedDrilldowns.filters) {
6044
- this.setRecommendedFilters(recommendedDrilldowns.filters);
6045
- }
6046
- } catch (error) {
6047
- console.error("Failed to fetch recommended drilldowns:", error);
6048
- }
6049
- }
6050
6239
  _updateScopesFilters() {
6051
6240
  var _a, _b;
6052
6241
  const scopes = sceneGraph.getScopes(this);
@@ -6096,10 +6285,10 @@ class AdHocFiltersVariable extends SceneObjectBase {
6096
6285
  }
6097
6286
  async verifyApplicabilityAndStoreRecentFilter(update) {
6098
6287
  await this._verifyApplicability();
6099
- if (!this.state.drilldownRecommendationsEnabled) {
6288
+ if (!this.state.drilldownRecommendationsEnabled || !this.state._valueRecommendations) {
6100
6289
  return;
6101
6290
  }
6102
- this.storeRecentFilter(update);
6291
+ this.state._valueRecommendations.storeRecentFilter(update);
6103
6292
  }
6104
6293
  setState(update) {
6105
6294
  var _a, _b;
@@ -6206,39 +6395,12 @@ class AdHocFiltersVariable extends SceneObjectBase {
6206
6395
  return f === filter ? { ...f, ...update } : f;
6207
6396
  });
6208
6397
  this.setState({ filters: updatedFilters });
6209
- this.storeRecentFilter({ ...filter, ...update });
6210
- }
6211
- storeRecentFilter(update) {
6212
- var _a;
6213
- if (!this.state.drilldownRecommendationsEnabled) {
6214
- return;
6215
- }
6216
- const key = getRecentFiltersKey((_a = this.state.datasource) == null ? void 0 : _a.uid);
6217
- const storedFilters = data.store.get(key);
6218
- const allRecentFilters = storedFilters ? JSON.parse(storedFilters) : [];
6219
- const updatedStoredFilters = [...allRecentFilters, update].slice(-10);
6220
- data.store.set(key, JSON.stringify(updatedStoredFilters));
6221
- const filter = this.state.filters.find((f) => f.key === update.key && !Boolean(f.nonApplicable));
6222
- if (filter && !Boolean(filter.nonApplicable)) {
6223
- this.setState({ _recentFilters: updatedStoredFilters.slice(-3) });
6224
- }
6225
- }
6226
- async _verifyRecentFiltersApplicability(storedFilters) {
6227
- const queries = this.state.useQueriesAsFilterForOptions ? getQueriesForVariables(this) : void 0;
6228
- const response = await this.getFiltersApplicabilityForQueries(storedFilters, queries != null ? queries : []);
6229
- if (!response) {
6230
- this.setState({ _recentFilters: storedFilters.slice(-3) });
6231
- return;
6398
+ if (this.state.drilldownRecommendationsEnabled && this.state._valueRecommendations) {
6399
+ this.state._valueRecommendations.storeRecentFilter({
6400
+ ...filter,
6401
+ ...update
6402
+ });
6232
6403
  }
6233
- const applicabilityMap = /* @__PURE__ */ new Map();
6234
- response.forEach((item) => {
6235
- applicabilityMap.set(item.key, item.applicable !== false);
6236
- });
6237
- const applicableFilters = storedFilters.filter((f) => {
6238
- const isApplicable = applicabilityMap.get(f.key);
6239
- return isApplicable === void 0 || isApplicable === true;
6240
- }).slice(-3);
6241
- this.setState({ _recentFilters: applicableFilters });
6242
6404
  }
6243
6405
  updateToMatchAll(filter) {
6244
6406
  this._updateFilter(filter, {
@@ -6315,11 +6477,6 @@ class AdHocFiltersVariable extends SceneObjectBase {
6315
6477
  });
6316
6478
  }
6317
6479
  }
6318
- setRecommendedFilters(recommendedFilters) {
6319
- this.setState({
6320
- _recommendedFilters: recommendedFilters
6321
- });
6322
- }
6323
6480
  async getFiltersApplicabilityForQueries(filters, queries) {
6324
6481
  const ds = await this._dataSourceSrv.get(this.state.datasource, this._scopedVars);
6325
6482
  if (!ds || !ds.getDrilldownsApplicability) {
@@ -6482,64 +6639,237 @@ class AdHocFiltersVariable extends SceneObjectBase {
6482
6639
  description
6483
6640
  }));
6484
6641
  }
6485
- }
6486
- AdHocFiltersVariable.Component = AdHocFiltersVariableRenderer;
6487
- function renderExpression(builder, filters) {
6488
- var _a;
6489
- return (builder != null ? builder : renderPrometheusLabelFilters)((_a = filters == null ? void 0 : filters.filter((f) => isFilterApplicable(f))) != null ? _a : []);
6490
- }
6491
- function AdHocFiltersVariableRenderer({ model }) {
6492
- const { filters, readOnly, addFilterButtonText } = model.useState();
6493
- const styles = ui.useStyles2(getStyles$b);
6494
- const controller = React.useMemo(
6495
- () => model.state.layout === "combobox" ? new AdHocFiltersVariableController(model) : void 0,
6496
- [model]
6497
- );
6498
- if (controller) {
6499
- return /* @__PURE__ */ React__default.default.createElement(AdHocFiltersComboboxRenderer, { controller });
6642
+ }
6643
+ AdHocFiltersVariable.Component = AdHocFiltersVariableRenderer;
6644
+ function renderExpression(builder, filters) {
6645
+ var _a;
6646
+ return (builder != null ? builder : renderPrometheusLabelFilters)((_a = filters == null ? void 0 : filters.filter((f) => isFilterApplicable(f))) != null ? _a : []);
6647
+ }
6648
+ function AdHocFiltersVariableRenderer({ model }) {
6649
+ const { filters, readOnly, addFilterButtonText } = model.useState();
6650
+ const styles = ui.useStyles2(getStyles$b);
6651
+ const controller = React.useMemo(
6652
+ () => model.state.layout === "combobox" ? new AdHocFiltersVariableController(model) : void 0,
6653
+ [model]
6654
+ );
6655
+ if (controller) {
6656
+ return /* @__PURE__ */ React__default.default.createElement(AdHocFiltersComboboxRenderer, { controller });
6657
+ }
6658
+ return /* @__PURE__ */ React__default.default.createElement("div", { className: styles.wrapper }, filters.filter((filter) => !filter.hidden).map((filter, index) => /* @__PURE__ */ React__default.default.createElement(React__default.default.Fragment, { key: index }, /* @__PURE__ */ React__default.default.createElement(AdHocFilterRenderer, { filter, model }))), !readOnly && /* @__PURE__ */ React__default.default.createElement(AdHocFilterBuilder, { model, key: "'builder", addFilterButtonText }));
6659
+ }
6660
+ const getStyles$b = (theme) => ({
6661
+ wrapper: css.css({
6662
+ display: "flex",
6663
+ flexWrap: "wrap",
6664
+ alignItems: "flex-end",
6665
+ columnGap: theme.spacing(2),
6666
+ rowGap: theme.spacing(1)
6667
+ })
6668
+ });
6669
+ function toSelectableValue$1(input) {
6670
+ const { text, value } = input;
6671
+ const result = {
6672
+ // converting text to string due to some edge cases where it can be a number
6673
+ // TODO: remove once https://github.com/grafana/grafana/issues/99021 is closed
6674
+ label: String(text),
6675
+ value: String(value != null ? value : text)
6676
+ };
6677
+ if ("group" in input) {
6678
+ result.group = input.group;
6679
+ }
6680
+ if ("meta" in input) {
6681
+ result.meta = input.meta;
6682
+ }
6683
+ return result;
6684
+ }
6685
+ function isMatchAllFilter(filter) {
6686
+ return filter.operator === "=~" && filter.value === ".*";
6687
+ }
6688
+ function isFilterComplete(filter) {
6689
+ return filter.key !== "" && filter.operator !== "" && filter.value !== "";
6690
+ }
6691
+ function isFilterApplicable(filter) {
6692
+ return !filter.nonApplicable;
6693
+ }
6694
+ function isMultiValueOperator(operatorValue) {
6695
+ const operator = OPERATORS.find((o) => o.value === operatorValue);
6696
+ if (!operator) {
6697
+ return false;
6698
+ }
6699
+ return Boolean(operator.isMulti);
6700
+ }
6701
+
6702
+ class GroupByRecommendations extends SceneObjectBase {
6703
+ constructor(parent) {
6704
+ super({});
6705
+ this._scopedVars = { __sceneObject: wrapInSafeSerializableSceneObject(this) };
6706
+ this._activationHandler = () => {
6707
+ const json = data.store.get(this._getStorageKey());
6708
+ const storedGroupings = json ? JSON.parse(json) : [];
6709
+ if (storedGroupings.length > 0) {
6710
+ this._verifyRecentGroupingsApplicability(storedGroupings);
6711
+ } else {
6712
+ this.setState({ recentGrouping: [] });
6713
+ }
6714
+ this._fetchRecommendedDrilldowns();
6715
+ const scopesVariable = sceneGraph.lookupVariable(SCOPES_VARIABLE_NAME, this);
6716
+ let scopesSubscription;
6717
+ if (scopesVariable instanceof ScopesVariable) {
6718
+ scopesSubscription = scopesVariable.subscribeToState((newState, prevState) => {
6719
+ if (newState.scopes !== prevState.scopes) {
6720
+ const json2 = data.store.get(this._getStorageKey());
6721
+ const storedGroupings2 = json2 ? JSON.parse(json2) : [];
6722
+ if (storedGroupings2.length > 0) {
6723
+ this._verifyRecentGroupingsApplicability(storedGroupings2);
6724
+ }
6725
+ }
6726
+ });
6727
+ }
6728
+ return () => {
6729
+ scopesSubscription == null ? void 0 : scopesSubscription.unsubscribe();
6730
+ };
6731
+ };
6732
+ this._parentVariable = parent;
6733
+ this.addActivationHandler(this._activationHandler);
6734
+ }
6735
+ /**
6736
+ * Get the parent variable
6737
+ */
6738
+ get parent() {
6739
+ return this._parentVariable;
6740
+ }
6741
+ _getStorageKey() {
6742
+ var _a;
6743
+ return getRecentGroupingKey((_a = this._parentVariable.state.datasource) == null ? void 0 : _a.uid);
6744
+ }
6745
+ async _fetchRecommendedDrilldowns() {
6746
+ const parent = this._parentVariable;
6747
+ const ds = await getDataSource(parent.state.datasource, this._scopedVars);
6748
+ if (!ds || !ds.getRecommendedDrilldowns) {
6749
+ return;
6750
+ }
6751
+ const queries = getQueriesForVariables(parent);
6752
+ const timeRange = sceneGraph.getTimeRange(this).state.value;
6753
+ const scopes = sceneGraph.getScopes(this);
6754
+ const groupByKeys = Array.isArray(parent.state.value) ? parent.state.value.map((v) => String(v)) : parent.state.value ? [String(parent.state.value)] : [];
6755
+ const enrichedRequest = getEnrichedDataRequest(this);
6756
+ const dashboardUid = enrichedRequest == null ? void 0 : enrichedRequest.dashboardUID;
6757
+ try {
6758
+ const recommendedDrilldowns = await ds.getRecommendedDrilldowns({
6759
+ timeRange,
6760
+ dashboardUid,
6761
+ queries,
6762
+ groupByKeys,
6763
+ scopes,
6764
+ userId: runtime.config.bootData.user.id
6765
+ });
6766
+ if (recommendedDrilldowns == null ? void 0 : recommendedDrilldowns.groupByKeys) {
6767
+ this.setState({
6768
+ recommendedGrouping: recommendedDrilldowns.groupByKeys.map((key) => ({ value: key, text: key }))
6769
+ });
6770
+ }
6771
+ } catch (error) {
6772
+ console.error("Failed to fetch recommended drilldowns:", error);
6773
+ }
6774
+ }
6775
+ async _verifyRecentGroupingsApplicability(storedGroupings) {
6776
+ const queries = getQueriesForVariables(this._parentVariable);
6777
+ const keys = storedGroupings.map((g) => String(g.value));
6778
+ const response = await this._getGroupByApplicabilityForQueries(keys, queries);
6779
+ if (!response) {
6780
+ this.setState({ recentGrouping: storedGroupings.slice(-3) });
6781
+ return;
6782
+ }
6783
+ const applicabilityMap = /* @__PURE__ */ new Map();
6784
+ response.forEach((item) => {
6785
+ applicabilityMap.set(item.key, item.applicable !== false);
6786
+ });
6787
+ const applicableGroupings = storedGroupings.filter((g) => {
6788
+ const isApplicable = applicabilityMap.get(String(g.value));
6789
+ return isApplicable === void 0 || isApplicable === true;
6790
+ }).slice(-3);
6791
+ this.setState({ recentGrouping: applicableGroupings });
6792
+ }
6793
+ async _getGroupByApplicabilityForQueries(value, queries) {
6794
+ const parent = this._parentVariable;
6795
+ const ds = await getDataSource(parent.state.datasource, this._scopedVars);
6796
+ if (!ds || !ds.getDrilldownsApplicability) {
6797
+ return;
6798
+ }
6799
+ const timeRange = sceneGraph.getTimeRange(this).state.value;
6800
+ return await ds.getDrilldownsApplicability({
6801
+ groupByKeys: Array.isArray(value) ? value.map((v) => String(v)) : value ? [String(value)] : [],
6802
+ queries,
6803
+ timeRange,
6804
+ scopes: sceneGraph.getScopes(this),
6805
+ ...getEnrichedFiltersRequest(this)
6806
+ });
6807
+ }
6808
+ /**
6809
+ * Stores recent groupings in localStorage and updates state.
6810
+ * Should be called by the parent variable when a grouping is added/updated.
6811
+ */
6812
+ storeRecentGrouping(applicableValues) {
6813
+ if (applicableValues.length === 0) {
6814
+ return;
6815
+ }
6816
+ const key = this._getStorageKey();
6817
+ const storedGroupings = data.store.get(key);
6818
+ const allRecentGroupings = storedGroupings ? JSON.parse(storedGroupings) : [];
6819
+ const existingWithoutApplicableValues = allRecentGroupings.filter(
6820
+ (grouping) => !applicableValues.includes(String(grouping.value))
6821
+ );
6822
+ const updatedStoredGroupings = [
6823
+ ...existingWithoutApplicableValues,
6824
+ ...applicableValues.map((value) => ({ value, text: value }))
6825
+ ];
6826
+ const limitedStoredGroupings = updatedStoredGroupings.slice(-10);
6827
+ data.store.set(key, JSON.stringify(limitedStoredGroupings));
6828
+ this.setState({ recentGrouping: limitedStoredGroupings.slice(-3) });
6500
6829
  }
6501
- return /* @__PURE__ */ React__default.default.createElement("div", { className: styles.wrapper }, filters.filter((filter) => !filter.hidden).map((filter, index) => /* @__PURE__ */ React__default.default.createElement(React__default.default.Fragment, { key: index }, /* @__PURE__ */ React__default.default.createElement(AdHocFilterRenderer, { filter, model }))), !readOnly && /* @__PURE__ */ React__default.default.createElement(AdHocFilterBuilder, { model, key: "'builder", addFilterButtonText }));
6502
- }
6503
- const getStyles$b = (theme) => ({
6504
- wrapper: css.css({
6505
- display: "flex",
6506
- flexWrap: "wrap",
6507
- alignItems: "flex-end",
6508
- columnGap: theme.spacing(2),
6509
- rowGap: theme.spacing(1)
6510
- })
6511
- });
6512
- function toSelectableValue$1(input) {
6513
- const { text, value } = input;
6514
- const result = {
6515
- // converting text to string due to some edge cases where it can be a number
6516
- // TODO: remove once https://github.com/grafana/grafana/issues/99021 is closed
6517
- label: String(text),
6518
- value: String(value != null ? value : text)
6519
- };
6520
- if ("group" in input) {
6521
- result.group = input.group;
6830
+ /**
6831
+ * Get the current values from the parent variable
6832
+ */
6833
+ getParentValues() {
6834
+ const parent = this._parentVariable;
6835
+ const value = lodash.isArray(parent.state.value) ? parent.state.value : [parent.state.value];
6836
+ const text = lodash.isArray(parent.state.text) ? parent.state.text.map(String) : [String(parent.state.text)];
6837
+ return { value, text };
6522
6838
  }
6523
- if ("meta" in input) {
6524
- result.meta = input.meta;
6839
+ /**
6840
+ * Add a grouping value to the parent variable
6841
+ */
6842
+ addValueToParent(newValue, newText) {
6843
+ const parent = this._parentVariable;
6844
+ const { value, text } = this.getParentValues();
6845
+ if (value.includes(newValue)) {
6846
+ return;
6847
+ }
6848
+ parent.changeValueTo(
6849
+ [...value.filter((v) => v !== ""), newValue],
6850
+ [...text.filter((t) => t !== ""), newText != null ? newText : String(newValue)],
6851
+ true
6852
+ );
6525
6853
  }
6526
- return result;
6527
- }
6528
- function isMatchAllFilter(filter) {
6529
- return filter.operator === "=~" && filter.value === ".*";
6530
- }
6531
- function isFilterComplete(filter) {
6532
- return filter.key !== "" && filter.operator !== "" && filter.value !== "";
6533
- }
6534
- function isFilterApplicable(filter) {
6535
- return !filter.nonApplicable;
6536
6854
  }
6537
- function isMultiValueOperator(operatorValue) {
6538
- const operator = OPERATORS.find((o) => o.value === operatorValue);
6539
- if (!operator) {
6540
- return false;
6541
- }
6542
- return Boolean(operator.isMulti);
6855
+ GroupByRecommendations.Component = GroupByRecommendationsRenderer;
6856
+ function GroupByRecommendationsRenderer({ model }) {
6857
+ const { recentGrouping, recommendedGrouping } = model.useState();
6858
+ const recentDrilldowns = recentGrouping == null ? void 0 : recentGrouping.map((groupBy) => ({
6859
+ label: `${groupBy.value}`,
6860
+ onClick: () => {
6861
+ var _a;
6862
+ model.addValueToParent(groupBy.value, (_a = groupBy.text) != null ? _a : String(groupBy.value));
6863
+ }
6864
+ }));
6865
+ const recommendedDrilldowns = recommendedGrouping == null ? void 0 : recommendedGrouping.map((groupBy) => ({
6866
+ label: `${groupBy.value}`,
6867
+ onClick: () => {
6868
+ var _a;
6869
+ model.addValueToParent(groupBy.value, (_a = groupBy.text) != null ? _a : String(groupBy.value));
6870
+ }
6871
+ }));
6872
+ return /* @__PURE__ */ React__default.default.createElement(DrilldownRecommendations, { recentDrilldowns, recommendedDrilldowns });
6543
6873
  }
6544
6874
 
6545
6875
  const getRecentGroupingKey = (datasourceUid) => `grafana.grouping.recent.${datasourceUid != null ? datasourceUid : "default"}`;
@@ -6562,22 +6892,16 @@ class GroupByVariable extends MultiValueVariable {
6562
6892
  this.isLazy = true;
6563
6893
  this._urlSync = new GroupByVariableUrlSyncHandler(this);
6564
6894
  this._activationHandler = () => {
6565
- var _a;
6566
6895
  this._verifyApplicability();
6567
6896
  if (this.state.defaultValue) {
6568
6897
  if (this.checkIfRestorable(this.state.value)) {
6569
6898
  this.setState({ restorable: true });
6570
6899
  }
6571
6900
  }
6572
- if (this.state.drilldownRecommendationsEnabled) {
6573
- const json = data.store.get(getRecentGroupingKey((_a = this.state.datasource) == null ? void 0 : _a.uid));
6574
- const storedGroupings = json ? JSON.parse(json) : [];
6575
- if (storedGroupings.length > 0) {
6576
- this._verifyRecentGroupingsApplicability(storedGroupings);
6577
- } else {
6578
- this.setState({ _recentGrouping: [] });
6579
- }
6580
- this._fetchRecommendedDrilldowns();
6901
+ if (this.state.drilldownRecommendationsEnabled && !this.state._valueRecommendations) {
6902
+ const valueRecommendations = new GroupByRecommendations(this);
6903
+ this.setState({ _valueRecommendations: valueRecommendations });
6904
+ valueRecommendations.activate();
6581
6905
  }
6582
6906
  return () => {
6583
6907
  if (this.state.defaultValue) {
@@ -6692,53 +7016,6 @@ class GroupByVariable extends MultiValueVariable {
6692
7016
  })
6693
7017
  );
6694
7018
  }
6695
- async _verifyRecentGroupingsApplicability(storedGroupings) {
6696
- const queries = getQueriesForVariables(this);
6697
- const keys = storedGroupings.map((g) => String(g.value));
6698
- const response = await this.getGroupByApplicabilityForQueries(keys, queries);
6699
- if (!response) {
6700
- this.setState({ _recentGrouping: storedGroupings.slice(-3) });
6701
- return;
6702
- }
6703
- const applicabilityMap = /* @__PURE__ */ new Map();
6704
- response.forEach((item) => {
6705
- applicabilityMap.set(item.key, item.applicable !== false);
6706
- });
6707
- const applicableGroupings = storedGroupings.filter((g) => {
6708
- const isApplicable = applicabilityMap.get(String(g.value));
6709
- return isApplicable === void 0 || isApplicable === true;
6710
- }).slice(-3);
6711
- this.setState({ _recentGrouping: applicableGroupings });
6712
- }
6713
- async _fetchRecommendedDrilldowns() {
6714
- const ds = await getDataSource(this.state.datasource, {
6715
- __sceneObject: wrapInSafeSerializableSceneObject(this)
6716
- });
6717
- if (!ds || !ds.getRecommendedDrilldowns) {
6718
- return;
6719
- }
6720
- const queries = getQueriesForVariables(this);
6721
- const timeRange = sceneGraph.getTimeRange(this).state.value;
6722
- const scopes = sceneGraph.getScopes(this);
6723
- const groupByKeys = Array.isArray(this.state.value) ? this.state.value.map((v) => String(v)) : this.state.value ? [String(this.state.value)] : [];
6724
- const enrichedRequest = getEnrichedDataRequest(this);
6725
- const dashboardUid = enrichedRequest == null ? void 0 : enrichedRequest.dashboardUID;
6726
- try {
6727
- const recommendedDrilldowns = await ds.getRecommendedDrilldowns({
6728
- timeRange,
6729
- dashboardUid,
6730
- queries,
6731
- groupByKeys,
6732
- scopes,
6733
- userId: runtime.config.bootData.user.id
6734
- });
6735
- if (recommendedDrilldowns == null ? void 0 : recommendedDrilldowns.groupByKeys) {
6736
- this.setRecommendedGrouping(recommendedDrilldowns.groupByKeys);
6737
- }
6738
- } catch (error) {
6739
- console.error("Failed to fetch recommended drilldowns:", error);
6740
- }
6741
- }
6742
7019
  getApplicableKeys() {
6743
7020
  const { value, keysApplicability } = this.state;
6744
7021
  const valueArray = lodash.isArray(value) ? value.map(String) : value ? [String(value)] : [];
@@ -6801,33 +7078,15 @@ class GroupByVariable extends MultiValueVariable {
6801
7078
  this.changeValueTo(this.state.defaultValue.value, this.state.defaultValue.text, true);
6802
7079
  }
6803
7080
  async _verifyApplicabilityAndStoreRecentGrouping() {
6804
- var _a;
6805
7081
  await this._verifyApplicability();
6806
- if (!this.state.drilldownRecommendationsEnabled) {
7082
+ if (!this.state.drilldownRecommendationsEnabled || !this.state._valueRecommendations) {
6807
7083
  return;
6808
7084
  }
6809
7085
  const applicableValues = this.getApplicableKeys();
6810
7086
  if (applicableValues.length === 0) {
6811
7087
  return;
6812
7088
  }
6813
- const key = getRecentGroupingKey((_a = this.state.datasource) == null ? void 0 : _a.uid);
6814
- const storedGroupings = data.store.get(key);
6815
- const allRecentGroupings = storedGroupings ? JSON.parse(storedGroupings) : [];
6816
- const existingWithoutApplicableValues = allRecentGroupings.filter(
6817
- (grouping) => !applicableValues.includes(String(grouping.value))
6818
- );
6819
- const updatedStoredGroupings = [
6820
- ...existingWithoutApplicableValues,
6821
- ...applicableValues.map((value) => ({ value, text: value }))
6822
- ];
6823
- const limitedStoredGroupings = updatedStoredGroupings.slice(-10);
6824
- data.store.set(key, JSON.stringify(limitedStoredGroupings));
6825
- this.setState({ _recentGrouping: limitedStoredGroupings.slice(-3) });
6826
- }
6827
- setRecommendedGrouping(recommendedGrouping) {
6828
- this.setState({
6829
- _recommendedGrouping: recommendedGrouping.map((key) => ({ value: key, text: key }))
6830
- });
7089
+ this.state._valueRecommendations.storeRecentGrouping(applicableValues);
6831
7090
  }
6832
7091
  /**
6833
7092
  * Allows clearing the value of the variable to an empty value. Overrides default behavior of a MultiValueVariable
@@ -6850,8 +7109,7 @@ function GroupByVariableRenderer({ model }) {
6850
7109
  allowCustomValue = true,
6851
7110
  defaultValue,
6852
7111
  keysApplicability,
6853
- _recentGrouping,
6854
- _recommendedGrouping,
7112
+ _valueRecommendations,
6855
7113
  drilldownRecommendationsEnabled
6856
7114
  } = model.useState();
6857
7115
  const styles = ui.useStyles2(getStyles$a);
@@ -7022,38 +7280,10 @@ function GroupByVariableRenderer({ model }) {
7022
7280
  }
7023
7281
  }
7024
7282
  );
7025
- if (!drilldownRecommendationsEnabled) {
7283
+ if (!drilldownRecommendationsEnabled || !_valueRecommendations) {
7026
7284
  return select;
7027
7285
  }
7028
- const recentDrilldowns = _recentGrouping == null ? void 0 : _recentGrouping.map((groupBy) => ({
7029
- label: `${groupBy.value}`,
7030
- onClick: () => {
7031
- var _a;
7032
- if (values.map((x) => x.value).includes(groupBy.value)) {
7033
- return;
7034
- }
7035
- model.changeValueTo(
7036
- [...values.map((x) => x.value), groupBy.value],
7037
- [...values.map((x) => x.label), (_a = groupBy.text) != null ? _a : groupBy.value],
7038
- true
7039
- );
7040
- }
7041
- }));
7042
- const recommendedDrilldowns = _recommendedGrouping == null ? void 0 : _recommendedGrouping.map((groupBy) => ({
7043
- label: `${groupBy.value}`,
7044
- onClick: () => {
7045
- var _a;
7046
- if (values.map((x) => x.value).includes(groupBy.value)) {
7047
- return;
7048
- }
7049
- model.changeValueTo(
7050
- [...values.map((x) => x.value), groupBy.value],
7051
- [...values.map((x) => x.label), (_a = groupBy.text) != null ? _a : groupBy.value],
7052
- true
7053
- );
7054
- }
7055
- }));
7056
- return /* @__PURE__ */ React__default.default.createElement("div", { className: styles.wrapper }, /* @__PURE__ */ React__default.default.createElement("div", { className: styles.recommendations }, /* @__PURE__ */ React__default.default.createElement(DrilldownRecommendations, { recentDrilldowns, recommendedDrilldowns })), select);
7286
+ return /* @__PURE__ */ React__default.default.createElement("div", { className: styles.wrapper }, /* @__PURE__ */ React__default.default.createElement("div", { className: styles.recommendations }, /* @__PURE__ */ React__default.default.createElement(_valueRecommendations.Component, { model: _valueRecommendations })), select);
7057
7287
  }
7058
7288
  const filterNoOp = () => true;
7059
7289
  function WideMenu(props) {
@@ -10306,91 +10536,6 @@ function containsSearchFilter(query) {
10306
10536
  return str.indexOf(SEARCH_FILTER_VARIABLE) > -1;
10307
10537
  }
10308
10538
 
10309
- class ScopesVariable extends SceneObjectBase {
10310
- constructor(state) {
10311
- super({
10312
- skipUrlSync: true,
10313
- loading: true,
10314
- scopes: [],
10315
- ...state,
10316
- type: "system",
10317
- name: SCOPES_VARIABLE_NAME,
10318
- hide: schema.VariableHide.hideVariable
10319
- });
10320
- this._renderBeforeActivation = true;
10321
- // Special options that enables variables to be hidden but still render to access react contexts
10322
- this.UNSAFE_renderAsHidden = true;
10323
- }
10324
- /**
10325
- * Temporary simple implementation to stringify the scopes.
10326
- */
10327
- getValue() {
10328
- var _a;
10329
- const scopes = (_a = this.state.scopes) != null ? _a : [];
10330
- return new ScopesVariableFormatter(scopes.map((scope) => scope.metadata.name));
10331
- }
10332
- getScopes() {
10333
- return this.state.scopes;
10334
- }
10335
- /**
10336
- * This method is used to keep the context up to date with the scopes context received from React
10337
- * 1) Subscribes to ScopesContext state changes and synchronizes it with the variable state
10338
- * 2) Handles enable / disabling of scopes based on variable enable option.
10339
- */
10340
- setContext(context) {
10341
- if (!context) {
10342
- return;
10343
- }
10344
- this._context = context;
10345
- const oldState = context.state;
10346
- if (this.state.enable != null) {
10347
- context.setEnabled(this.state.enable);
10348
- }
10349
- const sub = context.stateObservable.subscribe((state) => {
10350
- this.updateStateFromContext(state);
10351
- });
10352
- return () => {
10353
- sub.unsubscribe();
10354
- if (this.state.enable != null) {
10355
- context.setEnabled(oldState.enabled);
10356
- }
10357
- };
10358
- }
10359
- updateStateFromContext(state) {
10360
- const loading = state.value.length === 0 ? false : state.loading;
10361
- const oldScopes = this.state.scopes.map((scope) => scope.metadata.name);
10362
- const newScopes = state.value.map((scope) => scope.metadata.name);
10363
- const scopesHaveChanged = !lodash.isEqual(oldScopes, newScopes);
10364
- if (!loading && (scopesHaveChanged || newScopes.length === 0)) {
10365
- const queryController = getQueryController(this);
10366
- queryController == null ? void 0 : queryController.startProfile(SCOPES_CHANGED_INTERACTION);
10367
- this.setState({ scopes: state.value, loading });
10368
- this.publishEvent(new SceneVariableValueChangedEvent(this), true);
10369
- } else {
10370
- this.setState({ loading });
10371
- }
10372
- }
10373
- }
10374
- ScopesVariable.Component = ScopesVariableRenderer;
10375
- function ScopesVariableRenderer({ model }) {
10376
- const context = React.useContext(runtime.ScopesContext);
10377
- React.useEffect(() => {
10378
- return model.setContext(context);
10379
- }, [context, model]);
10380
- return null;
10381
- }
10382
- class ScopesVariableFormatter {
10383
- constructor(_value) {
10384
- this._value = _value;
10385
- }
10386
- formatter(formatNameOrFn) {
10387
- if (formatNameOrFn === schema.VariableFormatID.QueryParam) {
10388
- return this._value.map((scope) => `scope=${encodeURIComponent(scope)}`).join("&");
10389
- }
10390
- return this._value.join(", ");
10391
- }
10392
- }
10393
-
10394
10539
  function getVariables(sceneObject) {
10395
10540
  var _a;
10396
10541
  return (_a = getClosest(sceneObject, (s) => s.state.$variables)) != null ? _a : EmptyVariableSet;