@grafana/scenes 6.50.0--canary.1316.20252812955.0 → 6.50.0--canary.1312.20268893264.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.
Files changed (31) hide show
  1. package/dist/esm/packages/scenes/src/index.js +1 -1
  2. package/dist/esm/packages/scenes/src/index.js.map +1 -1
  3. package/dist/esm/packages/scenes/src/locales/en-US/grafana-scenes.json.js +7 -6
  4. package/dist/esm/packages/scenes/src/locales/en-US/grafana-scenes.json.js.map +1 -1
  5. package/dist/esm/packages/scenes/src/variables/DrilldownDependenciesManager.js.map +1 -1
  6. package/dist/esm/packages/scenes/src/variables/adhoc/AdHocFiltersCombobox/AdHocFiltersAlwaysWipCombobox.js +2 -2
  7. package/dist/esm/packages/scenes/src/variables/adhoc/AdHocFiltersCombobox/AdHocFiltersAlwaysWipCombobox.js.map +1 -1
  8. package/dist/esm/packages/scenes/src/variables/adhoc/AdHocFiltersCombobox/AdHocFiltersCombobox.js +4 -3
  9. package/dist/esm/packages/scenes/src/variables/adhoc/AdHocFiltersCombobox/AdHocFiltersCombobox.js.map +1 -1
  10. package/dist/esm/packages/scenes/src/variables/adhoc/AdHocFiltersCombobox/AdHocFiltersComboboxRenderer.js +23 -110
  11. package/dist/esm/packages/scenes/src/variables/adhoc/AdHocFiltersCombobox/AdHocFiltersComboboxRenderer.js.map +1 -1
  12. package/dist/esm/packages/scenes/src/variables/adhoc/AdHocFiltersCombobox/utils.js +0 -3
  13. package/dist/esm/packages/scenes/src/variables/adhoc/AdHocFiltersCombobox/utils.js.map +1 -1
  14. package/dist/esm/packages/scenes/src/variables/adhoc/AdHocFiltersRecommendations.js +151 -0
  15. package/dist/esm/packages/scenes/src/variables/adhoc/AdHocFiltersRecommendations.js.map +1 -0
  16. package/dist/esm/packages/scenes/src/variables/adhoc/AdHocFiltersVariable.js +28 -16
  17. package/dist/esm/packages/scenes/src/variables/adhoc/AdHocFiltersVariable.js.map +1 -1
  18. package/dist/esm/packages/scenes/src/variables/adhoc/controller/AdHocFiltersVariableController.js +5 -4
  19. package/dist/esm/packages/scenes/src/variables/adhoc/controller/AdHocFiltersVariableController.js.map +1 -1
  20. package/dist/esm/packages/scenes/src/variables/components/DrilldownRecommendations.js +102 -0
  21. package/dist/esm/packages/scenes/src/variables/components/DrilldownRecommendations.js.map +1 -0
  22. package/dist/esm/packages/scenes/src/variables/groupby/GroupByRecommendations.js +157 -0
  23. package/dist/esm/packages/scenes/src/variables/groupby/GroupByRecommendations.js.map +1 -0
  24. package/dist/esm/packages/scenes/src/variables/groupby/GroupByVariable.js +62 -13
  25. package/dist/esm/packages/scenes/src/variables/groupby/GroupByVariable.js.map +1 -1
  26. package/dist/{grafana-scenes-CwGWCTcQ.js → grafana-scenes-C9F_2hih.js} +8 -7
  27. package/dist/{grafana-scenes-CwGWCTcQ.js.map → grafana-scenes-C9F_2hih.js.map} +1 -1
  28. package/dist/index.d.ts +96 -19
  29. package/dist/index.js +585 -234
  30. package/dist/index.js.map +1 -1
  31. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -3458,7 +3458,7 @@ function wrapInSafeSerializableSceneObject(sceneObject) {
3458
3458
  function DefaultGroupByCustomIndicatorContainer(props) {
3459
3459
  const { model } = props;
3460
3460
  const theme = ui.useTheme2();
3461
- const styles = getStyles$j(theme);
3461
+ const styles = getStyles$l(theme);
3462
3462
  const inputStyles = ui.getInputStyles({ theme, invalid: false });
3463
3463
  const value = lodash.isArray(model.state.value) ? model.state.value : model.state.value ? [model.state.value] : [];
3464
3464
  let buttons = [];
@@ -3540,7 +3540,7 @@ function DefaultGroupByCustomIndicatorContainer(props) {
3540
3540
  buttons
3541
3541
  );
3542
3542
  }
3543
- const getStyles$j = (theme) => ({
3543
+ const getStyles$l = (theme) => ({
3544
3544
  clearIcon: css.css({
3545
3545
  color: theme.colors.action.disabledText,
3546
3546
  cursor: "pointer",
@@ -3614,6 +3614,469 @@ 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
+
3797
+ const getRecentFiltersKey = (datasourceUid) => `grafana.filters.recent.${datasourceUid != null ? datasourceUid : "default"}`;
3798
+ class AdHocFiltersRecommendations {
3799
+ constructor(adHocFilter, scopedVars) {
3800
+ this.adHocFilter = adHocFilter;
3801
+ this._scopedVars = scopedVars;
3802
+ }
3803
+ get recentFilters() {
3804
+ return this._recentFilters;
3805
+ }
3806
+ get recommendedFilters() {
3807
+ return this._recommendedFilters;
3808
+ }
3809
+ init() {
3810
+ const json = data.store.get(this._getStorageKey());
3811
+ const storedFilters = json ? JSON.parse(json) : [];
3812
+ if (storedFilters.length > 0) {
3813
+ this._verifyRecentFiltersApplicability(storedFilters);
3814
+ } else {
3815
+ this._recentFilters = [];
3816
+ }
3817
+ this._fetchRecommendedDrilldowns();
3818
+ const scopesVariable = sceneGraph.lookupVariable(SCOPES_VARIABLE_NAME, this.adHocFilter);
3819
+ if (scopesVariable instanceof ScopesVariable) {
3820
+ this.scopesSubscription = scopesVariable.subscribeToState((newState, prevState) => {
3821
+ if (newState.scopes !== prevState.scopes) {
3822
+ const json2 = data.store.get(this._getStorageKey());
3823
+ const storedFilters2 = json2 ? JSON.parse(json2) : [];
3824
+ if (storedFilters2.length > 0) {
3825
+ this._verifyRecentFiltersApplicability(storedFilters2);
3826
+ }
3827
+ this._fetchRecommendedDrilldowns();
3828
+ }
3829
+ });
3830
+ }
3831
+ }
3832
+ deinit() {
3833
+ var _a;
3834
+ (_a = this.scopesSubscription) == null ? void 0 : _a.unsubscribe();
3835
+ }
3836
+ _getStorageKey() {
3837
+ var _a;
3838
+ return getRecentFiltersKey((_a = this.adHocFilter.state.datasource) == null ? void 0 : _a.uid);
3839
+ }
3840
+ async _fetchRecommendedDrilldowns() {
3841
+ var _a;
3842
+ const adhoc = this.adHocFilter;
3843
+ const ds = await getDataSource(adhoc.state.datasource, this._scopedVars);
3844
+ if (!ds || !ds.getRecommendedDrilldowns) {
3845
+ return;
3846
+ }
3847
+ const queries = adhoc.state.useQueriesAsFilterForOptions ? getQueriesForVariables(adhoc) : void 0;
3848
+ const timeRange = sceneGraph.getTimeRange(adhoc).state.value;
3849
+ const scopes = sceneGraph.getScopes(adhoc);
3850
+ const filters = [...(_a = adhoc.state.originFilters) != null ? _a : [], ...adhoc.state.filters];
3851
+ const enrichedRequest = getEnrichedDataRequest(adhoc);
3852
+ const dashboardUid = enrichedRequest == null ? void 0 : enrichedRequest.dashboardUID;
3853
+ try {
3854
+ const recommendedDrilldowns = await ds.getRecommendedDrilldowns({
3855
+ timeRange,
3856
+ dashboardUid,
3857
+ queries: queries != null ? queries : [],
3858
+ filters,
3859
+ scopes
3860
+ });
3861
+ if (recommendedDrilldowns == null ? void 0 : recommendedDrilldowns.filters) {
3862
+ this._recommendedFilters = recommendedDrilldowns.filters;
3863
+ }
3864
+ } catch (error) {
3865
+ console.error("Failed to fetch recommended drilldowns:", error);
3866
+ }
3867
+ }
3868
+ async _verifyRecentFiltersApplicability(storedFilters) {
3869
+ const adhoc = this.adHocFilter;
3870
+ const queries = adhoc.state.useQueriesAsFilterForOptions ? getQueriesForVariables(adhoc) : void 0;
3871
+ const response = await adhoc.getFiltersApplicabilityForQueries(storedFilters, queries != null ? queries : []);
3872
+ if (!response) {
3873
+ this._recentFilters = storedFilters.slice(-3);
3874
+ return;
3875
+ }
3876
+ const applicabilityMap = /* @__PURE__ */ new Map();
3877
+ response.forEach((item) => {
3878
+ applicabilityMap.set(item.key, item.applicable !== false);
3879
+ });
3880
+ const applicableFilters = storedFilters.filter((f) => {
3881
+ const isApplicable = applicabilityMap.get(f.key);
3882
+ return isApplicable === void 0 || isApplicable === true;
3883
+ }).slice(-3);
3884
+ this._recentFilters = applicableFilters;
3885
+ }
3886
+ /**
3887
+ * Stores a recent filter in localStorage and updates state.
3888
+ * Should be called by the parent variable when a filter is added/updated.
3889
+ */
3890
+ storeRecentFilter(filter) {
3891
+ const key = this._getStorageKey();
3892
+ const storedFilters = data.store.get(key);
3893
+ const allRecentFilters = storedFilters ? JSON.parse(storedFilters) : [];
3894
+ const updatedStoredFilters = [...allRecentFilters, filter].slice(-10);
3895
+ data.store.set(key, JSON.stringify(updatedStoredFilters));
3896
+ const adhoc = this.adHocFilter;
3897
+ const existingFilter = adhoc.state.filters.find((f) => f.key === filter.key && !Boolean(f.nonApplicable));
3898
+ if (existingFilter && !Boolean(existingFilter.nonApplicable)) {
3899
+ this._recentFilters = updatedStoredFilters.slice(-3);
3900
+ }
3901
+ }
3902
+ addFilterToParent(filter) {
3903
+ this.adHocFilter.updateFilters([...this.adHocFilter.state.filters, filter]);
3904
+ }
3905
+ render() {
3906
+ var _a, _b;
3907
+ const { filters } = this.adHocFilter.useState();
3908
+ const recentDrilldowns = (_a = this.recentFilters) == null ? void 0 : _a.map((filter) => {
3909
+ var _a2;
3910
+ return {
3911
+ label: `${filter.key} ${filter.operator} ${((_a2 = filter.valueLabels) == null ? void 0 : _a2.length) ? filter.valueLabels.join(", ") : filter.value}`,
3912
+ onClick: () => {
3913
+ const exists = filters.some((f) => f.key === filter.key && f.value === filter.value);
3914
+ if (!exists) {
3915
+ this.addFilterToParent(filter);
3916
+ }
3917
+ }
3918
+ };
3919
+ });
3920
+ const recommendedDrilldowns = (_b = this.recommendedFilters) == null ? void 0 : _b.map((filter) => {
3921
+ var _a2;
3922
+ return {
3923
+ label: `${filter.key} ${filter.operator} ${((_a2 = filter.valueLabels) == null ? void 0 : _a2.length) ? filter.valueLabels.join(", ") : filter.value}`,
3924
+ onClick: () => {
3925
+ const exists = filters.some((f) => f.key === filter.key && f.value === filter.value);
3926
+ if (!exists) {
3927
+ this.addFilterToParent(filter);
3928
+ }
3929
+ }
3930
+ };
3931
+ });
3932
+ return /* @__PURE__ */ React__default.default.createElement(DrilldownRecommendations, { recentDrilldowns, recommendedDrilldowns });
3933
+ }
3934
+ }
3935
+
3936
+ const getRecentGroupingKey = (datasourceUid) => `grafana.grouping.recent.${datasourceUid != null ? datasourceUid : "default"}`;
3937
+ class GroupByRecommendations {
3938
+ constructor(groupByVariable, scopedVars) {
3939
+ this.groupBy = groupByVariable;
3940
+ this._scopedVars = scopedVars;
3941
+ }
3942
+ get recentGrouping() {
3943
+ return this._recentGrouping;
3944
+ }
3945
+ get recommendedGrouping() {
3946
+ return this._recommendedGrouping;
3947
+ }
3948
+ init() {
3949
+ const json = data.store.get(this._getStorageKey());
3950
+ const storedGroupings = json ? JSON.parse(json) : [];
3951
+ if (storedGroupings.length > 0) {
3952
+ this._verifyRecentGroupingsApplicability(storedGroupings);
3953
+ } else {
3954
+ this._recentGrouping = [];
3955
+ }
3956
+ this._fetchRecommendedDrilldowns();
3957
+ const scopesVariable = sceneGraph.lookupVariable(SCOPES_VARIABLE_NAME, this.groupBy);
3958
+ if (scopesVariable instanceof ScopesVariable) {
3959
+ this.scopesSubscription = scopesVariable.subscribeToState((newState, prevState) => {
3960
+ if (newState.scopes !== prevState.scopes) {
3961
+ const json2 = data.store.get(this._getStorageKey());
3962
+ const storedGroupings2 = json2 ? JSON.parse(json2) : [];
3963
+ if (storedGroupings2.length > 0) {
3964
+ this._verifyRecentGroupingsApplicability(storedGroupings2);
3965
+ }
3966
+ this._fetchRecommendedDrilldowns();
3967
+ }
3968
+ });
3969
+ }
3970
+ }
3971
+ deinit() {
3972
+ var _a;
3973
+ (_a = this.scopesSubscription) == null ? void 0 : _a.unsubscribe();
3974
+ }
3975
+ _getStorageKey() {
3976
+ var _a;
3977
+ return getRecentGroupingKey((_a = this.groupBy.state.datasource) == null ? void 0 : _a.uid);
3978
+ }
3979
+ async _fetchRecommendedDrilldowns() {
3980
+ const ds = await getDataSource(this.groupBy.state.datasource, this._scopedVars);
3981
+ if (!ds || !ds.getRecommendedDrilldowns) {
3982
+ return;
3983
+ }
3984
+ const queries = getQueriesForVariables(this.groupBy);
3985
+ const timeRange = sceneGraph.getTimeRange(this.groupBy).state.value;
3986
+ const scopes = sceneGraph.getScopes(this.groupBy);
3987
+ const groupByKeys = Array.isArray(this.groupBy.state.value) ? this.groupBy.state.value.map((v) => String(v)) : this.groupBy.state.value ? [String(this.groupBy.state.value)] : [];
3988
+ const enrichedRequest = getEnrichedDataRequest(this.groupBy);
3989
+ const dashboardUid = enrichedRequest == null ? void 0 : enrichedRequest.dashboardUID;
3990
+ try {
3991
+ const recommendedDrilldowns = await ds.getRecommendedDrilldowns({
3992
+ timeRange,
3993
+ dashboardUid,
3994
+ queries,
3995
+ groupByKeys,
3996
+ scopes
3997
+ });
3998
+ if (recommendedDrilldowns == null ? void 0 : recommendedDrilldowns.groupByKeys) {
3999
+ this._recommendedGrouping = recommendedDrilldowns.groupByKeys.map((key) => ({ value: key, text: key }));
4000
+ }
4001
+ } catch (error) {
4002
+ console.error("Failed to fetch recommended drilldowns:", error);
4003
+ }
4004
+ }
4005
+ async _verifyRecentGroupingsApplicability(storedGroupings) {
4006
+ const queries = getQueriesForVariables(this.groupBy);
4007
+ const keys = storedGroupings.map((g) => String(g.value));
4008
+ const response = await this.groupBy.getGroupByApplicabilityForQueries(keys, queries);
4009
+ if (!response) {
4010
+ this._recentGrouping = storedGroupings.slice(-3);
4011
+ return;
4012
+ }
4013
+ const applicabilityMap = /* @__PURE__ */ new Map();
4014
+ response.forEach((item) => {
4015
+ applicabilityMap.set(item.key, item.applicable !== false);
4016
+ });
4017
+ const applicableGroupings = storedGroupings.filter((g) => {
4018
+ const isApplicable = applicabilityMap.get(String(g.value));
4019
+ return isApplicable === void 0 || isApplicable === true;
4020
+ }).slice(-3);
4021
+ this._recentGrouping = applicableGroupings;
4022
+ }
4023
+ /**
4024
+ * Stores recent groupings in localStorage and updates state.
4025
+ * Should be called by the parent variable when a grouping is added/updated.
4026
+ */
4027
+ storeRecentGrouping(applicableValues) {
4028
+ if (applicableValues.length === 0) {
4029
+ return;
4030
+ }
4031
+ const key = this._getStorageKey();
4032
+ const storedGroupings = data.store.get(key);
4033
+ const allRecentGroupings = storedGroupings ? JSON.parse(storedGroupings) : [];
4034
+ const existingWithoutApplicableValues = allRecentGroupings.filter(
4035
+ (grouping) => !applicableValues.includes(String(grouping.value))
4036
+ );
4037
+ const updatedStoredGroupings = [
4038
+ ...existingWithoutApplicableValues,
4039
+ ...applicableValues.map((value) => ({ value, text: value }))
4040
+ ];
4041
+ const limitedStoredGroupings = updatedStoredGroupings.slice(-10);
4042
+ data.store.set(key, JSON.stringify(limitedStoredGroupings));
4043
+ this._recentGrouping = limitedStoredGroupings.slice(-3);
4044
+ }
4045
+ /**
4046
+ * Add a grouping value to the parent variable
4047
+ */
4048
+ addValueToParent(newValue, newText) {
4049
+ const value = lodash.isArray(this.groupBy.state.value) ? this.groupBy.state.value : [this.groupBy.state.value];
4050
+ const text = lodash.isArray(this.groupBy.state.text) ? this.groupBy.state.text.map(String) : [String(this.groupBy.state.text)];
4051
+ if (value.includes(newValue)) {
4052
+ return;
4053
+ }
4054
+ this.groupBy.changeValueTo(
4055
+ [...value.filter((v) => v !== ""), newValue],
4056
+ [...text.filter((t) => t !== ""), newText != null ? newText : String(newValue)],
4057
+ true
4058
+ );
4059
+ }
4060
+ render() {
4061
+ var _a, _b;
4062
+ const recentDrilldowns = (_a = this.recentGrouping) == null ? void 0 : _a.map((groupBy) => ({
4063
+ label: `${groupBy.value}`,
4064
+ onClick: () => {
4065
+ var _a2;
4066
+ this.addValueToParent(groupBy.value, (_a2 = groupBy.text) != null ? _a2 : String(groupBy.value));
4067
+ }
4068
+ }));
4069
+ const recommendedDrilldowns = (_b = this.recommendedGrouping) == null ? void 0 : _b.map((groupBy) => ({
4070
+ label: `${groupBy.value}`,
4071
+ onClick: () => {
4072
+ var _a2;
4073
+ this.addValueToParent(groupBy.value, (_a2 = groupBy.text) != null ? _a2 : String(groupBy.value));
4074
+ }
4075
+ }));
4076
+ return /* @__PURE__ */ React__default.default.createElement(DrilldownRecommendations, { recentDrilldowns, recommendedDrilldowns });
4077
+ }
4078
+ }
4079
+
3617
4080
  class GroupByVariable extends MultiValueVariable {
3618
4081
  constructor(initialState) {
3619
4082
  super({
@@ -3632,6 +4095,7 @@ class GroupByVariable extends MultiValueVariable {
3632
4095
  });
3633
4096
  this.isLazy = true;
3634
4097
  this._urlSync = new GroupByVariableUrlSyncHandler(this);
4098
+ this._scopedVars = { __sceneObject: wrapInSafeSerializableSceneObject(this) };
3635
4099
  this._activationHandler = () => {
3636
4100
  this._verifyApplicability();
3637
4101
  if (this.state.defaultValue) {
@@ -3639,11 +4103,18 @@ class GroupByVariable extends MultiValueVariable {
3639
4103
  this.setState({ restorable: true });
3640
4104
  }
3641
4105
  }
4106
+ if (this.state.drilldownRecommendationsEnabled && !this.state._valueRecommendations) {
4107
+ const valueRecommendations = new GroupByRecommendations(this, this._scopedVars);
4108
+ this.setState({ _valueRecommendations: valueRecommendations });
4109
+ valueRecommendations.init();
4110
+ }
3642
4111
  return () => {
4112
+ var _a;
3643
4113
  if (this.state.defaultValue) {
3644
4114
  this.restoreDefaultValues();
3645
4115
  }
3646
4116
  this.setState({ applicabilityEnabled: false });
4117
+ (_a = this.state._valueRecommendations) == null ? void 0 : _a.deinit();
3647
4118
  };
3648
4119
  };
3649
4120
  /**
@@ -3724,11 +4195,7 @@ class GroupByVariable extends MultiValueVariable {
3724
4195
  );
3725
4196
  }
3726
4197
  this.setState({ loading: true, error: null });
3727
- return rxjs.from(
3728
- getDataSource(this.state.datasource, {
3729
- __sceneObject: wrapInSafeSerializableSceneObject(this)
3730
- })
3731
- ).pipe(
4198
+ return rxjs.from(getDataSource(this.state.datasource, this._scopedVars)).pipe(
3732
4199
  rxjs.mergeMap((ds) => {
3733
4200
  return rxjs.from(this._getKeys(ds)).pipe(
3734
4201
  rxjs.tap((response) => {
@@ -3754,7 +4221,7 @@ class GroupByVariable extends MultiValueVariable {
3754
4221
  }
3755
4222
  getApplicableKeys() {
3756
4223
  const { value, keysApplicability } = this.state;
3757
- const valueArray = lodash.isArray(value) ? value : value ? [value] : [];
4224
+ const valueArray = lodash.isArray(value) ? value.map(String) : value ? [String(value)] : [];
3758
4225
  if (!keysApplicability || keysApplicability.length === 0) {
3759
4226
  return valueArray;
3760
4227
  }
@@ -3765,9 +4232,7 @@ class GroupByVariable extends MultiValueVariable {
3765
4232
  return applicableValues;
3766
4233
  }
3767
4234
  async getGroupByApplicabilityForQueries(value, queries) {
3768
- const ds = await getDataSource(this.state.datasource, {
3769
- __sceneObject: wrapInSafeSerializableSceneObject(this)
3770
- });
4235
+ const ds = await getDataSource(this.state.datasource, this._scopedVars);
3771
4236
  if (!ds.getDrilldownsApplicability) {
3772
4237
  return;
3773
4238
  }
@@ -3813,6 +4278,17 @@ class GroupByVariable extends MultiValueVariable {
3813
4278
  }
3814
4279
  this.changeValueTo(this.state.defaultValue.value, this.state.defaultValue.text, true);
3815
4280
  }
4281
+ async _verifyApplicabilityAndStoreRecentGrouping() {
4282
+ await this._verifyApplicability();
4283
+ if (!this.state.drilldownRecommendationsEnabled || !this.state._valueRecommendations) {
4284
+ return;
4285
+ }
4286
+ const applicableValues = this.getApplicableKeys();
4287
+ if (applicableValues.length === 0) {
4288
+ return;
4289
+ }
4290
+ this.state._valueRecommendations.storeRecentGrouping(applicableValues);
4291
+ }
3816
4292
  /**
3817
4293
  * Allows clearing the value of the variable to an empty value. Overrides default behavior of a MultiValueVariable
3818
4294
  */
@@ -3833,8 +4309,11 @@ function GroupByVariableRenderer({ model }) {
3833
4309
  includeAll,
3834
4310
  allowCustomValue = true,
3835
4311
  defaultValue,
3836
- keysApplicability
4312
+ keysApplicability,
4313
+ _valueRecommendations,
4314
+ drilldownRecommendationsEnabled
3837
4315
  } = model.useState();
4316
+ const styles = ui.useStyles2(getStyles$j);
3838
4317
  const values = React.useMemo(() => {
3839
4318
  const arrayValue = lodash.isArray(value) ? value : [value];
3840
4319
  const arrayText = lodash.isArray(text) ? text : [text];
@@ -3873,7 +4352,7 @@ function GroupByVariableRenderer({ model }) {
3873
4352
  () => handleOptionGroups(optionSearcher(inputValue).map(toSelectableValue$1)),
3874
4353
  [optionSearcher, inputValue]
3875
4354
  );
3876
- return isMulti ? /* @__PURE__ */ React__default.default.createElement(
4355
+ const select = isMulti ? /* @__PURE__ */ React__default.default.createElement(
3877
4356
  ui.MultiSelect,
3878
4357
  {
3879
4358
  "aria-label": i18n.t(
@@ -3887,6 +4366,7 @@ function GroupByVariableRenderer({ model }) {
3887
4366
  "Group by label"
3888
4367
  ),
3889
4368
  width: "auto",
4369
+ className: css.cx(drilldownRecommendationsEnabled && styles.selectStylesInWrapper),
3890
4370
  allowCustomValue,
3891
4371
  inputValue,
3892
4372
  value: uncommittedValue,
@@ -3920,7 +4400,7 @@ function GroupByVariableRenderer({ model }) {
3920
4400
  if (restorable !== model.state.restorable) {
3921
4401
  model.setState({ restorable });
3922
4402
  }
3923
- model._verifyApplicability();
4403
+ model._verifyApplicabilityAndStoreRecentGrouping();
3924
4404
  },
3925
4405
  onChange: (newValue, action) => {
3926
4406
  if (action.action === "clear" && noValueOnClear) {
@@ -3956,6 +4436,7 @@ function GroupByVariableRenderer({ model }) {
3956
4436
  "Group by label"
3957
4437
  ),
3958
4438
  width: "auto",
4439
+ className: css.cx(drilldownRecommendationsEnabled && styles.selectStylesInWrapper),
3959
4440
  inputValue,
3960
4441
  value: uncommittedValue && uncommittedValue.length > 0 ? uncommittedValue : null,
3961
4442
  allowCustomValue,
@@ -4000,6 +4481,10 @@ function GroupByVariableRenderer({ model }) {
4000
4481
  }
4001
4482
  }
4002
4483
  );
4484
+ if (!drilldownRecommendationsEnabled || !_valueRecommendations) {
4485
+ return select;
4486
+ }
4487
+ return /* @__PURE__ */ React__default.default.createElement("div", { className: styles.wrapper }, /* @__PURE__ */ React__default.default.createElement("div", { className: styles.recommendations }, _valueRecommendations.render()), select);
4003
4488
  }
4004
4489
  const filterNoOp$1 = () => true;
4005
4490
  function WideMenu(props) {
@@ -4016,6 +4501,31 @@ function toSelectableValue$1(input) {
4016
4501
  }
4017
4502
  return result;
4018
4503
  }
4504
+ const getStyles$j = (theme) => ({
4505
+ wrapper: css.css({
4506
+ display: "flex"
4507
+ }),
4508
+ selectStylesInWrapper: css.css({
4509
+ borderTopLeftRadius: 0,
4510
+ borderBottomLeftRadius: 0,
4511
+ border: `1px solid ${theme.colors.border.strong}`,
4512
+ borderLeft: "none"
4513
+ }),
4514
+ recommendations: css.css({
4515
+ display: "flex",
4516
+ alignItems: "center",
4517
+ paddingInline: theme.spacing(0.5),
4518
+ borderTop: `1px solid ${theme.colors.border.strong}`,
4519
+ borderBottom: `1px solid ${theme.colors.border.strong}`,
4520
+ backgroundColor: theme.components.input.background,
4521
+ "& button": {
4522
+ borderRadius: 0,
4523
+ height: "100%",
4524
+ margin: 0,
4525
+ paddingInline: theme.spacing(0.5)
4526
+ }
4527
+ })
4528
+ });
4019
4529
 
4020
4530
  function VizPanelSeriesLimit({ data, showAll, seriesLimit, onShowAllSeries }) {
4021
4531
  const styles = ui.useStyles2(getStyles$i);
@@ -6661,9 +7171,6 @@ const generatePlaceholder = (filter, filterInputType, isMultiValueEdit, isAlways
6661
7171
  }
6662
7172
  return ((_a = filter.valueLabels) == null ? void 0 : _a[0]) || "";
6663
7173
  }
6664
- if (isAlwaysWip && filterInputType === "operator") {
6665
- return "";
6666
- }
6667
7174
  return filter[filterInputType] && !isAlwaysWip ? `${filter[filterInputType]}` : inputPlaceholder || INPUT_PLACEHOLDER_DEFAULT;
6668
7175
  };
6669
7176
  const populateInputValueOnInputTypeSwitch = ({
@@ -6852,8 +7359,7 @@ const AdHocCombobox = React.forwardRef(function AdHocCombobox2({
6852
7359
  isAlwaysWip,
6853
7360
  handleChangeViewMode,
6854
7361
  focusOnWipInputRef,
6855
- populateInputOnEdit,
6856
- onInputClick
7362
+ populateInputOnEdit
6857
7363
  }, parentRef) {
6858
7364
  var _a, _b, _c;
6859
7365
  const [open, setOpen] = React.useState(false);
@@ -7336,7 +7842,6 @@ const AdHocCombobox = React.forwardRef(function AdHocCombobox2({
7336
7842
  className: css.cx(styles.inputStyle, { [styles.loadingInputPadding]: !optionsLoading }),
7337
7843
  onClick: (event) => {
7338
7844
  event.stopPropagation();
7339
- onInputClick == null ? void 0 : onInputClick();
7340
7845
  setOpen(true);
7341
7846
  },
7342
7847
  onFocus: () => {
@@ -7464,6 +7969,9 @@ const AdHocCombobox = React.forwardRef(function AdHocCombobox2({
7464
7969
  {
7465
7970
  onApply: () => {
7466
7971
  handleMultiValueFilterCommit(controller, filter, filterMultiValues);
7972
+ handleResetWip();
7973
+ handleChangeViewMode == null ? void 0 : handleChangeViewMode();
7974
+ setOpen(false);
7467
7975
  },
7468
7976
  floatingElement: refs.floating.current,
7469
7977
  maxOptionWidth,
@@ -7795,102 +8303,53 @@ const getStyles$a = (theme) => ({
7795
8303
  ...getNonApplicablePillStyles(theme)
7796
8304
  });
7797
8305
 
7798
- const AdHocFiltersAlwaysWipCombobox = React.forwardRef(function AdHocFiltersAlwaysWipCombobox2({ controller, onInputClick }, parentRef) {
8306
+ const AdHocFiltersAlwaysWipCombobox = React.forwardRef(function AdHocFiltersAlwaysWipCombobox2({ controller }, parentRef) {
7799
8307
  const { wip } = controller.useState();
7800
8308
  React.useLayoutEffect(() => {
7801
8309
  if (!wip) {
7802
8310
  controller.addWip();
7803
8311
  }
7804
8312
  }, [wip]);
7805
- return /* @__PURE__ */ React__default.default.createElement(AdHocCombobox, { controller, filter: wip, isAlwaysWip: true, ref: parentRef, onInputClick });
8313
+ return /* @__PURE__ */ React__default.default.createElement(AdHocCombobox, { controller, filter: wip, isAlwaysWip: true, ref: parentRef });
7806
8314
  });
7807
8315
 
7808
- const MAX_VISIBLE_FILTERS = 4;
7809
8316
  const AdHocFiltersComboboxRenderer = React.memo(function AdHocFiltersComboboxRenderer2({ controller }) {
7810
- var _a;
7811
- const { originFilters, filters, readOnly, collapsible } = controller.useState();
8317
+ const { originFilters, filters, readOnly, valueRecommendations, drilldownRecommendationsEnabled } = controller.useState();
7812
8318
  const styles = ui.useStyles2(getStyles$9);
7813
- const theme = ui.useTheme2();
7814
- const [collapsed, setCollapsed] = React.useState(true);
7815
- const [wrapperRef, { height: wrapperHeight }] = reactUse.useMeasure();
7816
- const clearAll = () => {
7817
- controller.clearAll();
7818
- };
7819
8319
  const focusOnWipInputRef = React.useRef();
7820
- const singleLineThreshold = theme.spacing.gridSize * 5;
7821
- const isMultiLine = collapsible && wrapperHeight > singleLineThreshold;
7822
- const handleCollapseToggle = (event) => {
7823
- event.stopPropagation();
7824
- if (collapsible) {
7825
- setCollapsed(true);
7826
- }
7827
- };
7828
- const handleExpand = () => {
7829
- var _a2, _b;
7830
- if (!collapsible) {
7831
- (_a2 = focusOnWipInputRef.current) == null ? void 0 : _a2.call(focusOnWipInputRef);
7832
- return;
7833
- }
7834
- if (collapsed) {
7835
- setCollapsed(false);
7836
- } else {
7837
- (_b = focusOnWipInputRef.current) == null ? void 0 : _b.call(focusOnWipInputRef);
7838
- }
7839
- };
7840
- const visibleOriginFilters = (_a = originFilters == null ? void 0 : originFilters.filter((f) => f.origin)) != null ? _a : [];
7841
- const visibleFilters = filters.filter((f) => !f.hidden);
7842
- const allFilters = [...visibleOriginFilters, ...visibleFilters];
7843
- const totalFiltersCount = allFilters.length;
7844
- const shouldCollapse = collapsible && collapsed && totalFiltersCount > 0;
7845
- const filtersToRender = shouldCollapse ? allFilters.slice(0, MAX_VISIBLE_FILTERS) : allFilters;
7846
- React.useEffect(() => {
7847
- if (collapsible && totalFiltersCount === 0 && collapsed) {
7848
- setCollapsed(false);
7849
- }
7850
- }, [collapsible, totalFiltersCount, collapsed]);
7851
- const showCollapseButton = collapsible && isMultiLine && !collapsed;
7852
8320
  return /* @__PURE__ */ React__default.default.createElement(
7853
8321
  "div",
7854
8322
  {
7855
- ref: wrapperRef,
7856
- className: css.cx(styles.comboboxWrapper, {
7857
- [styles.comboboxFocusOutline]: !readOnly,
7858
- [styles.collapsed]: shouldCollapse,
7859
- [styles.clickableCollapsed]: shouldCollapse
7860
- }),
7861
- onClick: handleExpand
8323
+ className: css.cx(styles.comboboxWrapper, { [styles.comboboxFocusOutline]: !readOnly }),
8324
+ onClick: () => {
8325
+ var _a;
8326
+ (_a = focusOnWipInputRef.current) == null ? void 0 : _a.call(focusOnWipInputRef);
8327
+ }
7862
8328
  },
7863
8329
  /* @__PURE__ */ React__default.default.createElement(ui.Icon, { name: "filter", className: styles.filterIcon, size: "lg" }),
7864
- filtersToRender.map((filter, index) => /* @__PURE__ */ React__default.default.createElement(
8330
+ drilldownRecommendationsEnabled && valueRecommendations && valueRecommendations.render(),
8331
+ originFilters == null ? void 0 : originFilters.map(
8332
+ (filter, index) => filter.origin ? /* @__PURE__ */ React__default.default.createElement(
8333
+ AdHocFilterPill,
8334
+ {
8335
+ key: `${index}-${filter.key}`,
8336
+ filter,
8337
+ controller,
8338
+ focusOnWipInputRef: focusOnWipInputRef.current
8339
+ }
8340
+ ) : null
8341
+ ),
8342
+ filters.filter((filter) => !filter.hidden).map((filter, index) => /* @__PURE__ */ React__default.default.createElement(
7865
8343
  AdHocFilterPill,
7866
8344
  {
7867
- key: `${filter.origin ? "origin-" : ""}${index}-${filter.key}`,
8345
+ key: `${index}-${filter.key}`,
7868
8346
  filter,
7869
8347
  controller,
7870
8348
  readOnly: readOnly || filter.readOnly,
7871
8349
  focusOnWipInputRef: focusOnWipInputRef.current
7872
8350
  }
7873
8351
  )),
7874
- shouldCollapse && totalFiltersCount > MAX_VISIBLE_FILTERS && /* @__PURE__ */ React__default.default.createElement("span", { className: styles.moreIndicator }, i18n.t("grafana-scenes.variables.adhoc-filters-combobox-renderer.more-filters", "+{{count}} more", {
7875
- count: totalFiltersCount - MAX_VISIBLE_FILTERS
7876
- })),
7877
- !readOnly && !shouldCollapse ? /* @__PURE__ */ React__default.default.createElement(AdHocFiltersAlwaysWipCombobox, { controller, ref: focusOnWipInputRef }) : null,
7878
- showCollapseButton && /* @__PURE__ */ React__default.default.createElement(
7879
- ui.Button,
7880
- {
7881
- className: styles.collapseButton,
7882
- fill: "text",
7883
- onClick: handleCollapseToggle,
7884
- "aria-label": i18n.t(
7885
- "grafana-scenes.variables.adhoc-filters-combobox-renderer.collapse-filters",
7886
- "Collapse filters"
7887
- ),
7888
- "aria-expanded": !collapsed
7889
- },
7890
- i18n.t("grafana-scenes.variables.adhoc-filters-combobox-renderer.collapse", "Collapse"),
7891
- /* @__PURE__ */ React__default.default.createElement(ui.Icon, { name: "angle-up", size: "md" })
7892
- ),
7893
- /* @__PURE__ */ React__default.default.createElement("div", { className: styles.clearAllButton }, /* @__PURE__ */ React__default.default.createElement(ui.Icon, { name: "times", size: "md", onClick: clearAll }))
8352
+ !readOnly ? /* @__PURE__ */ React__default.default.createElement(AdHocFiltersAlwaysWipCombobox, { controller, ref: focusOnWipInputRef }) : null
7894
8353
  );
7895
8354
  });
7896
8355
  const getStyles$9 = (theme) => ({
@@ -7922,42 +8381,6 @@ const getStyles$9 = (theme) => ({
7922
8381
  filterIcon: css.css({
7923
8382
  color: theme.colors.text.secondary,
7924
8383
  alignSelf: "center"
7925
- }),
7926
- collapsed: css.css({
7927
- flexWrap: "nowrap",
7928
- overflow: "hidden"
7929
- }),
7930
- clickableCollapsed: css.css({
7931
- cursor: "pointer",
7932
- "&:hover": {
7933
- borderColor: theme.colors.border.medium
7934
- }
7935
- }),
7936
- moreIndicator: css.css({
7937
- color: theme.colors.text.secondary,
7938
- whiteSpace: "nowrap",
7939
- ...theme.typography.bodySmall,
7940
- fontStyle: "italic",
7941
- marginLeft: theme.spacing(0.5)
7942
- }),
7943
- collapseButton: css.css({
7944
- marginLeft: "auto",
7945
- color: theme.colors.text.secondary,
7946
- padding: 0,
7947
- fontSize: theme.typography.bodySmall.fontSize,
7948
- border: "none",
7949
- "&:hover": {
7950
- background: "transparent",
7951
- color: theme.colors.text.primary
7952
- }
7953
- }),
7954
- clearAllButton: css.css({
7955
- fontSize: theme.typography.bodySmall.fontSize,
7956
- cursor: "pointer",
7957
- color: theme.colors.text.secondary,
7958
- "&:hover": {
7959
- color: theme.colors.text.primary
7960
- }
7961
8384
  })
7962
8385
  });
7963
8386
 
@@ -8062,7 +8485,8 @@ class AdHocFiltersVariableController {
8062
8485
  supportsMultiValueOperators: state.supportsMultiValueOperators,
8063
8486
  onAddCustomValue: state.onAddCustomValue,
8064
8487
  wip: state._wip,
8065
- collapsible: state.collapsible
8488
+ valueRecommendations: state._valueRecommendations,
8489
+ drilldownRecommendationsEnabled: state.drilldownRecommendationsEnabled
8066
8490
  };
8067
8491
  }
8068
8492
  async getKeys(currentKey) {
@@ -8077,6 +8501,9 @@ class AdHocFiltersVariableController {
8077
8501
  updateFilter(filter, update) {
8078
8502
  this.model._updateFilter(filter, update);
8079
8503
  }
8504
+ updateFilters(filters, options) {
8505
+ this.model.updateFilters(filters, options);
8506
+ }
8080
8507
  updateToMatchAll(filter) {
8081
8508
  this.model.updateToMatchAll(filter);
8082
8509
  }
@@ -8095,9 +8522,6 @@ class AdHocFiltersVariableController {
8095
8522
  restoreOriginalFilter(filter) {
8096
8523
  this.model.restoreOriginalFilter(filter);
8097
8524
  }
8098
- clearAll() {
8099
- this.model.clearAll();
8100
- }
8101
8525
  startProfile(name) {
8102
8526
  const queryController = getQueryController(this.model);
8103
8527
  queryController == null ? void 0 : queryController.startProfile(name);
@@ -8187,14 +8611,20 @@ class AdHocFiltersVariable extends SceneObjectBase {
8187
8611
  this._debouncedVerifyApplicability = lodash.debounce(this._verifyApplicability, 100);
8188
8612
  this._activationHandler = () => {
8189
8613
  this._debouncedVerifyApplicability();
8614
+ if (this.state.drilldownRecommendationsEnabled && !this.state._valueRecommendations) {
8615
+ const valueRecommendations = new AdHocFiltersRecommendations(this, this._scopedVars);
8616
+ this.setState({ _valueRecommendations: valueRecommendations });
8617
+ valueRecommendations.init();
8618
+ }
8190
8619
  return () => {
8191
- var _a;
8620
+ var _a, _b;
8192
8621
  (_a = this.state.originFilters) == null ? void 0 : _a.forEach((filter) => {
8193
8622
  if (filter.restorable) {
8194
8623
  this.restoreOriginalFilter(filter);
8195
8624
  }
8196
8625
  });
8197
8626
  this.setState({ applicabilityEnabled: false });
8627
+ (_b = this.state._valueRecommendations) == null ? void 0 : _b.deinit();
8198
8628
  };
8199
8629
  };
8200
8630
  if (this.state.applyMode === "auto") {
@@ -8256,6 +8686,13 @@ class AdHocFiltersVariable extends SceneObjectBase {
8256
8686
  this._prevScopes = scopes;
8257
8687
  this._debouncedVerifyApplicability();
8258
8688
  }
8689
+ async verifyApplicabilityAndStoreRecentFilter(update) {
8690
+ await this._verifyApplicability();
8691
+ if (!this.state.drilldownRecommendationsEnabled || !this.state._valueRecommendations) {
8692
+ return;
8693
+ }
8694
+ this.state._valueRecommendations.storeRecentFilter(update);
8695
+ }
8259
8696
  setState(update) {
8260
8697
  var _a, _b;
8261
8698
  let filterExpressionChanged = false;
@@ -8314,18 +8751,6 @@ class AdHocFiltersVariable extends SceneObjectBase {
8314
8751
  this._updateFilter(filter, original);
8315
8752
  }
8316
8753
  }
8317
- /**
8318
- * Clear all user-added filters and restore origin filters to their original values.
8319
- */
8320
- clearAll() {
8321
- var _a;
8322
- (_a = this.state.originFilters) == null ? void 0 : _a.forEach((filter) => {
8323
- if (filter.restorable) {
8324
- this.restoreOriginalFilter(filter);
8325
- }
8326
- });
8327
- this.setState({ filters: [] });
8328
- }
8329
8754
  getValue(fieldPath) {
8330
8755
  if (fieldPath === ORIGIN_FILTERS_KEY) {
8331
8756
  const originFilters = this.state.originFilters;
@@ -8359,8 +8784,11 @@ class AdHocFiltersVariable extends SceneObjectBase {
8359
8784
  }
8360
8785
  if (filter === _wip) {
8361
8786
  if ("value" in update && update["value"] !== "") {
8362
- this.setState({ filters: [...filters, { ..._wip, ...update }], _wip: void 0 });
8363
- this._debouncedVerifyApplicability();
8787
+ this.setState({
8788
+ filters: [...filters, { ..._wip, ...update }],
8789
+ _wip: void 0
8790
+ });
8791
+ this.verifyApplicabilityAndStoreRecentFilter({ ..._wip, ...update });
8364
8792
  } else {
8365
8793
  this.setState({ _wip: { ...filter, ...update } });
8366
8794
  }
@@ -8370,6 +8798,12 @@ class AdHocFiltersVariable extends SceneObjectBase {
8370
8798
  return f === filter ? { ...f, ...update } : f;
8371
8799
  });
8372
8800
  this.setState({ filters: updatedFilters });
8801
+ if (this.state.drilldownRecommendationsEnabled && this.state._valueRecommendations) {
8802
+ this.state._valueRecommendations.storeRecentFilter({
8803
+ ...filter,
8804
+ ...update
8805
+ });
8806
+ }
8373
8807
  }
8374
8808
  updateToMatchAll(filter) {
8375
8809
  this._updateFilter(filter, {
@@ -8560,11 +8994,12 @@ class AdHocFiltersVariable extends SceneObjectBase {
8560
8994
  let scopes = sceneGraph.getScopes(this);
8561
8995
  if (filter.origin === "scope") {
8562
8996
  scopes = scopes == null ? void 0 : scopes.map((scope) => {
8997
+ var _a2;
8563
8998
  return {
8564
8999
  ...scope,
8565
9000
  spec: {
8566
9001
  ...scope.spec,
8567
- filters: scope.spec.filters.filter((f) => f.key !== filter.key)
9002
+ filters: (_a2 = scope.spec.filters) == null ? void 0 : _a2.filter((f) => f.key !== filter.key)
8568
9003
  }
8569
9004
  };
8570
9005
  });
@@ -10053,91 +10488,6 @@ function containsSearchFilter(query) {
10053
10488
  return str.indexOf(SEARCH_FILTER_VARIABLE) > -1;
10054
10489
  }
10055
10490
 
10056
- class ScopesVariable extends SceneObjectBase {
10057
- constructor(state) {
10058
- super({
10059
- skipUrlSync: true,
10060
- loading: true,
10061
- scopes: [],
10062
- ...state,
10063
- type: "system",
10064
- name: SCOPES_VARIABLE_NAME,
10065
- hide: schema.VariableHide.hideVariable
10066
- });
10067
- this._renderBeforeActivation = true;
10068
- // Special options that enables variables to be hidden but still render to access react contexts
10069
- this.UNSAFE_renderAsHidden = true;
10070
- }
10071
- /**
10072
- * Temporary simple implementation to stringify the scopes.
10073
- */
10074
- getValue() {
10075
- var _a;
10076
- const scopes = (_a = this.state.scopes) != null ? _a : [];
10077
- return new ScopesVariableFormatter(scopes.map((scope) => scope.metadata.name));
10078
- }
10079
- getScopes() {
10080
- return this.state.scopes;
10081
- }
10082
- /**
10083
- * This method is used to keep the context up to date with the scopes context received from React
10084
- * 1) Subscribes to ScopesContext state changes and synchronizes it with the variable state
10085
- * 2) Handles enable / disabling of scopes based on variable enable option.
10086
- */
10087
- setContext(context) {
10088
- if (!context) {
10089
- return;
10090
- }
10091
- this._context = context;
10092
- const oldState = context.state;
10093
- if (this.state.enable != null) {
10094
- context.setEnabled(this.state.enable);
10095
- }
10096
- const sub = context.stateObservable.subscribe((state) => {
10097
- this.updateStateFromContext(state);
10098
- });
10099
- return () => {
10100
- sub.unsubscribe();
10101
- if (this.state.enable != null) {
10102
- context.setEnabled(oldState.enabled);
10103
- }
10104
- };
10105
- }
10106
- updateStateFromContext(state) {
10107
- const loading = state.value.length === 0 ? false : state.loading;
10108
- const oldScopes = this.state.scopes.map((scope) => scope.metadata.name);
10109
- const newScopes = state.value.map((scope) => scope.metadata.name);
10110
- const scopesHaveChanged = !lodash.isEqual(oldScopes, newScopes);
10111
- if (!loading && (scopesHaveChanged || newScopes.length === 0)) {
10112
- const queryController = getQueryController(this);
10113
- queryController == null ? void 0 : queryController.startProfile(SCOPES_CHANGED_INTERACTION);
10114
- this.setState({ scopes: state.value, loading });
10115
- this.publishEvent(new SceneVariableValueChangedEvent(this), true);
10116
- } else {
10117
- this.setState({ loading });
10118
- }
10119
- }
10120
- }
10121
- ScopesVariable.Component = ScopesVariableRenderer;
10122
- function ScopesVariableRenderer({ model }) {
10123
- const context = React.useContext(runtime.ScopesContext);
10124
- React.useEffect(() => {
10125
- return model.setContext(context);
10126
- }, [context, model]);
10127
- return null;
10128
- }
10129
- class ScopesVariableFormatter {
10130
- constructor(_value) {
10131
- this._value = _value;
10132
- }
10133
- formatter(formatNameOrFn) {
10134
- if (formatNameOrFn === schema.VariableFormatID.QueryParam) {
10135
- return this._value.map((scope) => `scope=${encodeURIComponent(scope)}`).join("&");
10136
- }
10137
- return this._value.join(", ");
10138
- }
10139
- }
10140
-
10141
10491
  function getVariables(sceneObject) {
10142
10492
  var _a;
10143
10493
  return (_a = getClosest(sceneObject, (s) => s.state.$variables)) != null ? _a : EmptyVariableSet;
@@ -16454,7 +16804,7 @@ function __variableDynamicImportRuntime0__(path) {
16454
16804
  switch (path) {
16455
16805
  case '../locales/cs-CZ/grafana-scenes.json': return Promise.resolve().then(function () { return require('./grafana-scenes-DPdlSPjz.js'); });
16456
16806
  case '../locales/de-DE/grafana-scenes.json': return Promise.resolve().then(function () { return require('./grafana-scenes-BHIE4ld0.js'); });
16457
- case '../locales/en-US/grafana-scenes.json': return Promise.resolve().then(function () { return require('./grafana-scenes-CwGWCTcQ.js'); });
16807
+ case '../locales/en-US/grafana-scenes.json': return Promise.resolve().then(function () { return require('./grafana-scenes-C9F_2hih.js'); });
16458
16808
  case '../locales/es-ES/grafana-scenes.json': return Promise.resolve().then(function () { return require('./grafana-scenes-D4tq59Dc.js'); });
16459
16809
  case '../locales/fr-FR/grafana-scenes.json': return Promise.resolve().then(function () { return require('./grafana-scenes-Ce77KCbO.js'); });
16460
16810
  case '../locales/hu-HU/grafana-scenes.json': return Promise.resolve().then(function () { return require('./grafana-scenes-DMYCnFop.js'); });
@@ -16599,6 +16949,7 @@ exports.dataLayers = index;
16599
16949
  exports.escapeUrlPipeDelimiters = escapeUrlPipeDelimiters;
16600
16950
  exports.formatRegistry = formatRegistry;
16601
16951
  exports.getExploreURL = getExploreURL;
16952
+ exports.getQueriesForVariables = getQueriesForVariables;
16602
16953
  exports.isCustomVariableValue = isCustomVariableValue;
16603
16954
  exports.isDataLayer = isDataLayer;
16604
16955
  exports.isDataRequestEnricher = isDataRequestEnricher;