@gooddata/sdk-ui-dashboard 11.62.0-alpha.1 → 11.62.0-alpha.3
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/NOTICE +40 -618
- package/esm/__version.d.ts +1 -1
- package/esm/__version.js +1 -1
- package/esm/_staging/dashboard/dashboardFilterContext.d.ts.map +1 -1
- package/esm/_staging/dashboard/dashboardFilterContext.js +4 -2
- package/esm/_staging/dashboard/dashboardFilterContextValidation.js +1 -1
- package/esm/index.d.ts +5 -3
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +1 -1
- package/esm/kdaDialog/composition/KdaAttributesDropdown.d.ts +2 -1
- package/esm/kdaDialog/composition/KdaAttributesDropdown.d.ts.map +1 -1
- package/esm/kdaDialog/composition/KdaAttributesDropdown.js +4 -5
- package/esm/kdaDialog/composition/KeyDriversFooter.d.ts.map +1 -1
- package/esm/kdaDialog/composition/KeyDriversFooter.js +12 -4
- package/esm/model/commandHandlers/dashboard/common/filterViews.d.ts +3 -2
- package/esm/model/commandHandlers/dashboard/common/filterViews.d.ts.map +1 -1
- package/esm/model/commandHandlers/dashboard/common/filterViews.js +12 -6
- package/esm/model/commandHandlers/dashboard/common/stateInitializers.d.ts.map +1 -1
- package/esm/model/commandHandlers/dashboard/common/stateInitializers.js +5 -6
- package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/index.d.ts.map +1 -1
- package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/index.js +1 -1
- package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/loadUnavailableReferences.d.ts.map +1 -1
- package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/loadUnavailableReferences.js +2 -0
- package/esm/model/commandHandlers/dashboard/resetDashboardHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/dashboard/resetDashboardHandler.js +3 -1
- package/esm/model/commandHandlers/dashboard/saveDashboardHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/dashboard/saveDashboardHandler.js +27 -10
- package/esm/model/commandHandlers/drill/drillToDashboardHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/drill/drillToDashboardHandler.js +4 -1
- package/esm/model/commandHandlers/drill/keyDriverAnalysisHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/drill/keyDriverAnalysisHandler.js +5 -3
- package/esm/model/commandHandlers/drill/resolveDrillToCustomUrl.d.ts +21 -1
- package/esm/model/commandHandlers/drill/resolveDrillToCustomUrl.d.ts.map +1 -1
- package/esm/model/commandHandlers/drill/resolveDrillToCustomUrl.js +56 -54
- package/esm/model/commandHandlers/filterContext/attributeFilter/changeAttributeFilterSelectionHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/filterContext/attributeFilter/changeAttributeFilterSelectionHandler.js +17 -4
- package/esm/model/commandHandlers/filterContext/attributeFilter/replaceAttributeFilterItemSelectionHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/filterContext/attributeFilter/replaceAttributeFilterItemSelectionHandler.js +6 -1
- package/esm/model/commandHandlers/filterContext/changeFilterContextSelectionHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/filterContext/changeFilterContextSelectionHandler.js +188 -70
- package/esm/model/commandHandlers/filterContext/common.d.ts +14 -2
- package/esm/model/commandHandlers/filterContext/common.d.ts.map +1 -1
- package/esm/model/commandHandlers/filterContext/common.js +43 -6
- package/esm/model/commandHandlers/filterContext/dateFilter/changeDateFilterSelectionHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/filterContext/dateFilter/changeDateFilterSelectionHandler.js +13 -3
- package/esm/model/commandHandlers/filterContext/measureValueFilter/changeMeasureValueFilterConditionHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/filterContext/measureValueFilter/changeMeasureValueFilterConditionHandler.js +11 -2
- package/esm/model/commandHandlers/filterContext/measureValueFilter/changeMeasureValueFilterDimensionalityHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/filterContext/measureValueFilter/changeMeasureValueFilterDimensionalityHandler.js +3 -0
- package/esm/model/commandHandlers/parameters/changeParameterValuesHandler.d.ts +10 -2
- package/esm/model/commandHandlers/parameters/changeParameterValuesHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/parameters/changeParameterValuesHandler.js +20 -3
- package/esm/model/commandHandlers/scheduledEmail/initializeAutomationsHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/scheduledEmail/initializeAutomationsHandler.js +21 -4
- package/esm/model/commandHandlers/tabs/createDashboardTabHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/tabs/createDashboardTabHandler.js +27 -3
- package/esm/model/commands/filters.d.ts +16 -1
- package/esm/model/commands/filters.d.ts.map +1 -1
- package/esm/model/commands/filters.js +4 -3
- package/esm/model/commands/parameters.d.ts +4 -3
- package/esm/model/commands/parameters.d.ts.map +1 -1
- package/esm/model/commands/parameters.js +3 -2
- package/esm/model/events/dashboard.d.ts +17 -1
- package/esm/model/events/dashboard.d.ts.map +1 -1
- package/esm/model/events/dashboard.js +2 -1
- package/esm/model/store/drill/drillSelectors.d.ts +6 -0
- package/esm/model/store/drill/drillSelectors.d.ts.map +1 -1
- package/esm/model/store/drill/drillSelectors.js +9 -0
- package/esm/model/store/filtering/dashboardFilterSelectors.d.ts +6 -1
- package/esm/model/store/filtering/dashboardFilterSelectors.d.ts.map +1 -1
- package/esm/model/store/filtering/dashboardFilterSelectors.js +21 -19
- package/esm/model/store/filtering/restrictedFilterUtils.d.ts +26 -1
- package/esm/model/store/filtering/restrictedFilterUtils.d.ts.map +1 -1
- package/esm/model/store/filtering/restrictedFilterUtils.js +74 -6
- package/esm/model/store/meta/metaSelectors.d.ts +6 -0
- package/esm/model/store/meta/metaSelectors.d.ts.map +1 -1
- package/esm/model/store/meta/metaSelectors.js +13 -3
- package/esm/model/store/tabs/filterContext/filterContextReducers.d.ts +48 -0
- package/esm/model/store/tabs/filterContext/filterContextReducers.d.ts.map +1 -1
- package/esm/model/store/tabs/filterContext/filterContextReducers.js +362 -102
- package/esm/model/store/tabs/filterContext/filterContextSelectors.d.ts +74 -1
- package/esm/model/store/tabs/filterContext/filterContextSelectors.d.ts.map +1 -1
- package/esm/model/store/tabs/filterContext/filterContextSelectors.js +27 -21
- package/esm/model/store/tabs/filterContext/filterSync.d.ts +101 -0
- package/esm/model/store/tabs/filterContext/filterSync.d.ts.map +1 -0
- package/esm/model/store/tabs/filterContext/filterSync.js +361 -0
- package/esm/model/store/tabs/index.d.ts +2 -0
- package/esm/model/store/tabs/index.d.ts.map +1 -1
- package/esm/model/store/tabs/parameters/parametersReducers.d.ts +5 -0
- package/esm/model/store/tabs/parameters/parametersReducers.d.ts.map +1 -1
- package/esm/model/store/tabs/parameters/parametersReducers.js +9 -5
- package/esm/model/store/tabs/tabsReducers.d.ts +6 -0
- package/esm/model/store/tabs/tabsReducers.d.ts.map +1 -1
- package/esm/model/store/tabs/tabsReducers.js +14 -6
- package/esm/model/store/tabs/tabsState.d.ts +10 -0
- package/esm/model/store/tabs/tabsState.d.ts.map +1 -1
- package/esm/model/store/tabs/tabsState.js +20 -0
- package/esm/model/store/unavailableObjects/copyBlockingReferences.d.ts +5 -3
- package/esm/model/store/unavailableObjects/copyBlockingReferences.d.ts.map +1 -1
- package/esm/model/store/unavailableObjects/copyBlockingReferences.js +9 -16
- package/esm/model/store/unavailableObjects/unavailableObjectsSelectors.d.ts +0 -16
- package/esm/model/store/unavailableObjects/unavailableObjectsSelectors.d.ts.map +1 -1
- package/esm/model/store/unavailableObjects/unavailableObjectsSelectors.js +0 -18
- package/esm/model/utils/attributeFilterMatching.d.ts +38 -0
- package/esm/model/utils/attributeFilterMatching.d.ts.map +1 -0
- package/esm/model/utils/attributeFilterMatching.js +110 -0
- package/esm/model/utils/attributeFilterSelectionTypeCompatibility.d.ts.map +1 -0
- package/esm/presentation/automations/alerting/state/types.d.ts +2 -2
- package/esm/presentation/automations/alerting/state/types.d.ts.map +1 -1
- package/esm/presentation/automations/connectors/hooks/useBuildAutomationsContext.d.ts.map +1 -1
- package/esm/presentation/automations/connectors/hooks/useBuildAutomationsContext.js +7 -3
- package/esm/presentation/automations/contexts/AutomationsContext.d.ts +6 -2
- package/esm/presentation/automations/contexts/AutomationsContext.d.ts.map +1 -1
- package/esm/presentation/automations/scheduledEmail/state/types.d.ts +2 -2
- package/esm/presentation/automations/scheduledEmail/state/types.d.ts.map +1 -1
- package/esm/presentation/automations/scheduledEmail/types.d.ts +2 -2
- package/esm/presentation/automations/scheduledEmail/types.d.ts.map +1 -1
- package/esm/presentation/automations/shared/automationFilters/automationParameters.d.ts +11 -1
- package/esm/presentation/automations/shared/automationFilters/automationParameters.d.ts.map +1 -1
- package/esm/presentation/automations/shared/automationFilters/automationParameters.js +2 -0
- package/esm/presentation/automations/shared/automationFilters/components/AutomationFiltersSelect.d.ts +3 -3
- package/esm/presentation/automations/shared/automationFilters/components/AutomationFiltersSelect.d.ts.map +1 -1
- package/esm/presentation/automations/shared/automationFilters/components/AutomationFiltersSelect.js +3 -13
- package/esm/presentation/automations/shared/automationFilters/hooks/useDefaultSelectedFiltersForExistingAutomation.d.ts +1 -1
- package/esm/presentation/automations/shared/automationFilters/hooks/useDefaultSelectedFiltersForExistingAutomation.d.ts.map +1 -1
- package/esm/presentation/automations/shared/automationFilters/hooks/useDefaultSelectedFiltersForExistingAutomation.js +1 -1
- package/esm/presentation/automations/shared/automationFilters/hooks/useValidateExistingAutomationFilters.d.ts.map +1 -1
- package/esm/presentation/automations/shared/automationFilters/hooks/useValidateExistingAutomationFilters.js +1 -1
- package/esm/presentation/automations/shared/automationFilters/useAutomationAlertParameters.d.ts +2 -2
- package/esm/presentation/automations/shared/automationFilters/useAutomationAlertParameters.d.ts.map +1 -1
- package/esm/presentation/automations/shared/automationFilters/useAutomationExportParameters.d.ts +6 -2
- package/esm/presentation/automations/shared/automationFilters/useAutomationExportParameters.d.ts.map +1 -1
- package/esm/presentation/automations/shared/automationFilters/useAutomationFilters.d.ts.map +1 -1
- package/esm/presentation/automations/shared/automationFilters/useAutomationFilters.js +2 -2
- package/esm/presentation/automations/shared/automationFilters/utils.d.ts.map +1 -1
- package/esm/presentation/automations/shared/automationFilters/utils.js +8 -0
- package/esm/presentation/automations/shared/filters/conversions.d.ts +1 -1
- package/esm/presentation/automations/shared/filters/conversions.d.ts.map +1 -1
- package/esm/presentation/automations/shared/filters/conversions.js +5 -3
- package/esm/presentation/automations/shared/slots/types.d.ts +2 -2
- package/esm/presentation/automations/shared/slots/types.d.ts.map +1 -1
- package/esm/presentation/dashboardSettingsDialog/useDialogData.js +1 -1
- package/esm/presentation/filterBar/attributeFilter/DefaultDashboardAttributeFilter.d.ts.map +1 -1
- package/esm/presentation/filterBar/attributeFilter/DefaultDashboardAttributeFilter.js +1 -4
- package/esm/presentation/filterBar/attributeFilter/addAttributeFilter/AttributesDropdown.d.ts +2 -11
- package/esm/presentation/filterBar/attributeFilter/addAttributeFilter/AttributesDropdown.d.ts.map +1 -1
- package/esm/presentation/filterBar/attributeFilter/addAttributeFilter/AttributesDropdown.js +17 -16
- package/esm/presentation/filterBar/attributeFilter/addAttributeFilter/staticListItems.d.ts +11 -0
- package/esm/presentation/filterBar/attributeFilter/addAttributeFilter/staticListItems.d.ts.map +1 -0
- package/esm/presentation/filterBar/attributeFilter/addAttributeFilter/staticListItems.js +7 -0
- package/esm/presentation/filterBar/attributeFilter/addAttributeFilter/useMetricDropdownItems.d.ts +2 -11
- package/esm/presentation/filterBar/attributeFilter/addAttributeFilter/useMetricDropdownItems.d.ts.map +1 -1
- package/esm/presentation/filterBar/attributeFilter/addAttributeFilter/useMetricDropdownItems.js +0 -6
- package/esm/presentation/filterBar/attributeFilter/addAttributeFilter/useParameterDropdownItems.d.ts +34 -0
- package/esm/presentation/filterBar/attributeFilter/addAttributeFilter/useParameterDropdownItems.d.ts.map +1 -0
- package/esm/presentation/filterBar/attributeFilter/addAttributeFilter/useParameterDropdownItems.js +41 -0
- package/esm/presentation/filterBar/attributeFilter/dashboardDropdownBody/configuration/hooks/useLimitingItemsConfiguration.d.ts.map +1 -1
- package/esm/presentation/filterBar/attributeFilter/dashboardDropdownBody/configuration/hooks/useLimitingItemsConfiguration.js +4 -17
- package/esm/presentation/filterBar/attributeFilter/useDependentDateFilters.d.ts.map +1 -1
- package/esm/presentation/filterBar/attributeFilter/useDependentDateFilters.js +4 -13
- package/esm/presentation/filterBar/attributeFilter/useParentFilters.d.ts.map +1 -1
- package/esm/presentation/filterBar/attributeFilter/useParentFilters.js +3 -10
- package/esm/presentation/filterBar/filterBar/DefaultDashboardFilterGroup.d.ts.map +1 -1
- package/esm/presentation/filterBar/filterBar/DefaultDashboardFilterGroup.js +0 -9
- package/esm/presentation/filterBar/filterBar/DefaultFilterBarItem.d.ts.map +1 -1
- package/esm/presentation/filterBar/filterBar/DefaultFilterBarItem.js +15 -3
- package/esm/presentation/filterBar/filterBar/hooks/useResetFiltersButton.d.ts.map +1 -1
- package/esm/presentation/filterBar/filterBar/hooks/useResetFiltersButton.js +4 -0
- package/esm/presentation/localization/bundles/en-US.localization-bundle.d.ts +8 -0
- package/esm/presentation/localization/bundles/en-US.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/en-US.localization-bundle.js +8 -0
- package/esm/presentation/widget/insight/ViewModeDashboardInsight/InsightDrillDialog/DrillDialogExportDropdown.d.ts.map +1 -1
- package/esm/presentation/widget/insight/ViewModeDashboardInsight/InsightDrillDialog/DrillDialogExportDropdown.js +1 -1
- package/esm/presentation/widget/insight/configuration/DrillFilters/drillFiltersConfigUtils.d.ts +2 -3
- package/esm/presentation/widget/insight/configuration/DrillFilters/drillFiltersConfigUtils.d.ts.map +1 -1
- package/esm/presentation/widget/insight/configuration/DrillFilters/drillFiltersConfigUtils.js +29 -133
- package/esm/presentation/widget/insight/configuration/DrillFilters/optionMappings/mapDashboardFilterToOption.d.ts.map +1 -1
- package/esm/presentation/widget/insight/configuration/DrillFilters/optionMappings/mapDashboardFilterToOption.js +1 -1
- package/esm/presentation/widget/insight/configuration/DrillFilters/optionMappings/mapSourceInsightFilterToOption.d.ts.map +1 -1
- package/esm/presentation/widget/insight/configuration/DrillFilters/optionMappings/mapSourceInsightFilterToOption.js +1 -1
- package/esm/presentation/widget/insight/configuration/DrillFilters/optionMappings/mapSourceMeasureFilterToOption.d.ts.map +1 -1
- package/esm/presentation/widget/insight/configuration/DrillFilters/optionMappings/mapSourceMeasureFilterToOption.js +1 -1
- package/esm/sdk-ui-dashboard.d.ts +151 -13
- package/package.json +23 -23
- package/styles/css/drillDialog.css +4 -0
- package/styles/css/drillDialog.css.map +1 -1
- package/styles/css/main.css +8 -0
- package/styles/css/main.css.map +1 -1
- package/styles/scss/drillDialog.scss +5 -0
- package/esm/model/commandHandlers/dashboard/common/attributeFilterSelectionTypeCompatibility.d.ts.map +0 -1
- /package/esm/model/{commandHandlers/dashboard/common → utils}/attributeFilterSelectionTypeCompatibility.d.ts +0 -0
- /package/esm/model/{commandHandlers/dashboard/common → utils}/attributeFilterSelectionTypeCompatibility.js +0 -0
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
// (C) 2026 GoodData Corporation
|
|
2
|
+
import { areObjRefsEqual, dashboardAttributeFilterItemFilterElementsBy, dashboardAttributeFilterItemFilterElementsByDate, dashboardAttributeFilterItemLocalIdentifier, dashboardAttributeFilterItemValidateElementsBy, dashboardFilterLocalIdentifier, isDashboardArbitraryAttributeFilter, isDashboardAttributeFilter, isDashboardAttributeFilterItem, isDashboardCommonDateFilter, isDashboardDateFilter, isDashboardMatchAttributeFilter, isDashboardMeasureValueFilter, } from "@gooddata/sdk-model";
|
|
3
|
+
import { findAttributeFilterCounterpart } from "../../../utils/attributeFilterMatching.js";
|
|
4
|
+
/**
|
|
5
|
+
* True when `localIdentifier` belongs to a cross-filtering virtual filter on `tab`.
|
|
6
|
+
* Those filters are applied on their own tab only and are not mirrored to or from other tabs.
|
|
7
|
+
*
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
export function isCrossFilteringVirtualFilter(tab, localIdentifier, crossFilteringLocalIdsByTab) {
|
|
11
|
+
if (!localIdentifier) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
return crossFilteringLocalIdsByTab?.[tab.localIdentifier]?.includes(localIdentifier) ?? false;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Finds the filter on `targetTab` that is the same logical filter as `sourceFilter`.
|
|
18
|
+
* Same-type matches take precedence over list/text migrations. A missing or incompatible
|
|
19
|
+
* counterpart is a no-op for the caller.
|
|
20
|
+
*
|
|
21
|
+
* Assumes at most one filter per display form, date data set, and measure with a given
|
|
22
|
+
* dimensionality on a tab.
|
|
23
|
+
*
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
export function findCounterpartFilter(sourceTab, sourceFilter, targetTab, attributesWithReferences, options) {
|
|
27
|
+
const targets = appliedFilters(targetTab).filter((filter) => !isCrossFilteringVirtualFilter(targetTab, dashboardFilterLocalIdentifier(filter), options?.crossFilteringLocalIdsByTab));
|
|
28
|
+
if (targets.length === 0) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
const displayForms = collectDisplayForms(sourceTab, targetTab, attributesWithReferences);
|
|
32
|
+
if (isDashboardAttributeFilterItem(sourceFilter)) {
|
|
33
|
+
const result = findAttributeFilterCounterpart(sourceFilter, targets.filter(isDashboardAttributeFilterItem), {
|
|
34
|
+
sourceFilterConfigs: sourceTab.attributeFilterConfigs?.attributeFilterConfigs,
|
|
35
|
+
targetFilterConfigs: targetTab.attributeFilterConfigs?.attributeFilterConfigs,
|
|
36
|
+
displayForms,
|
|
37
|
+
attributesWithReferences,
|
|
38
|
+
allowListToText: options?.allowListToText ?? true,
|
|
39
|
+
});
|
|
40
|
+
return result.status === "matched" ? result.counterpart : undefined;
|
|
41
|
+
}
|
|
42
|
+
if (isDashboardDateFilter(sourceFilter)) {
|
|
43
|
+
return findDateCounterpart(sourceFilter, targets);
|
|
44
|
+
}
|
|
45
|
+
if (isDashboardMeasureValueFilter(sourceFilter)) {
|
|
46
|
+
return findMeasureValueCounterpart(sourceFilter, targets);
|
|
47
|
+
}
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* The saved initial filter on `tab` that corresponds to `appliedFilter`.
|
|
52
|
+
* Same-tab `localIdentifier` wins; identity matching is the fallback.
|
|
53
|
+
* Returns undefined when this tab has no saved counterpart.
|
|
54
|
+
*
|
|
55
|
+
* @internal
|
|
56
|
+
*/
|
|
57
|
+
export function findOriginalFilter(tab, appliedFilter) {
|
|
58
|
+
const originals = tab.filterContext?.originalFilterContextDefinition?.filters;
|
|
59
|
+
if (!originals?.length) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
const localIdentifier = dashboardFilterLocalIdentifier(appliedFilter);
|
|
63
|
+
if (localIdentifier) {
|
|
64
|
+
const byLocalId = originals.find((filter) => dashboardFilterLocalIdentifier(filter) === localIdentifier);
|
|
65
|
+
if (byLocalId) {
|
|
66
|
+
return byLocalId;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (!tab.filterContext?.originalFilterContextDefinition) {
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
const originalTab = {
|
|
73
|
+
...tab,
|
|
74
|
+
filterContext: {
|
|
75
|
+
...tab.filterContext,
|
|
76
|
+
filterContextDefinition: tab.filterContext.originalFilterContextDefinition,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
return findCounterpartFilter(tab, appliedFilter, originalTab);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Rewrites an attribute filter item so it keeps `localIdentifier` (the target tab's id).
|
|
83
|
+
*
|
|
84
|
+
* @internal
|
|
85
|
+
*/
|
|
86
|
+
export function retargetAttributeFilterItem(filter, localIdentifier) {
|
|
87
|
+
if (isDashboardAttributeFilter(filter)) {
|
|
88
|
+
return { attributeFilter: { ...filter.attributeFilter, localIdentifier } };
|
|
89
|
+
}
|
|
90
|
+
if (isDashboardArbitraryAttributeFilter(filter)) {
|
|
91
|
+
return { arbitraryAttributeFilter: { ...filter.arbitraryAttributeFilter, localIdentifier } };
|
|
92
|
+
}
|
|
93
|
+
if (isDashboardMatchAttributeFilter(filter)) {
|
|
94
|
+
return { matchAttributeFilter: { ...filter.matchAttributeFilter, localIdentifier } };
|
|
95
|
+
}
|
|
96
|
+
return filter;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Carries over parent-filter dependencies from the filter being replaced.
|
|
100
|
+
*
|
|
101
|
+
* @internal
|
|
102
|
+
*/
|
|
103
|
+
export function preserveFilterDependencies(existing, replacement) {
|
|
104
|
+
const filterElementsBy = dashboardAttributeFilterItemFilterElementsBy(existing);
|
|
105
|
+
const filterElementsByDate = dashboardAttributeFilterItemFilterElementsByDate(existing);
|
|
106
|
+
const validateElementsBy = dashboardAttributeFilterItemValidateElementsBy(existing);
|
|
107
|
+
if (!filterElementsBy?.length && !filterElementsByDate?.length && !validateElementsBy?.length) {
|
|
108
|
+
return replacement;
|
|
109
|
+
}
|
|
110
|
+
const preserved = {
|
|
111
|
+
...(filterElementsBy?.length ? { filterElementsBy } : {}),
|
|
112
|
+
...(filterElementsByDate?.length ? { filterElementsByDate } : {}),
|
|
113
|
+
...(validateElementsBy?.length ? { validateElementsBy } : {}),
|
|
114
|
+
};
|
|
115
|
+
if (isDashboardAttributeFilter(replacement)) {
|
|
116
|
+
return { attributeFilter: { ...replacement.attributeFilter, ...preserved } };
|
|
117
|
+
}
|
|
118
|
+
if (isDashboardArbitraryAttributeFilter(replacement)) {
|
|
119
|
+
return { arbitraryAttributeFilter: { ...replacement.arbitraryAttributeFilter, ...preserved } };
|
|
120
|
+
}
|
|
121
|
+
return replacement;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Copies an applied source filter onto an existing target filter, keeping the target's
|
|
125
|
+
* local identifier, parent dependencies, and measure-value dimensionality. Does not create a filter.
|
|
126
|
+
*
|
|
127
|
+
* @internal
|
|
128
|
+
*/
|
|
129
|
+
export function applyMirroredFilterValue(source, target) {
|
|
130
|
+
if (isDashboardAttributeFilter(source) && isDashboardAttributeFilter(target)) {
|
|
131
|
+
return {
|
|
132
|
+
attributeFilter: {
|
|
133
|
+
...target.attributeFilter,
|
|
134
|
+
attributeElements: source.attributeFilter.attributeElements,
|
|
135
|
+
negativeSelection: source.attributeFilter.negativeSelection,
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
if (isDashboardDateFilter(source) && isDashboardDateFilter(target)) {
|
|
140
|
+
const dateFilter = {
|
|
141
|
+
...source.dateFilter,
|
|
142
|
+
};
|
|
143
|
+
if (target.dateFilter.localIdentifier) {
|
|
144
|
+
dateFilter.localIdentifier = target.dateFilter.localIdentifier;
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
delete dateFilter.localIdentifier;
|
|
148
|
+
}
|
|
149
|
+
if (target.dateFilter.dataSet) {
|
|
150
|
+
dateFilter.dataSet = target.dateFilter.dataSet;
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
delete dateFilter.dataSet;
|
|
154
|
+
}
|
|
155
|
+
return { dateFilter };
|
|
156
|
+
}
|
|
157
|
+
if (isDashboardMeasureValueFilter(source) && isDashboardMeasureValueFilter(target)) {
|
|
158
|
+
const { measure, localIdentifier, title, dimensionality } = target.dashboardMeasureValueFilter;
|
|
159
|
+
return {
|
|
160
|
+
dashboardMeasureValueFilter: {
|
|
161
|
+
measure,
|
|
162
|
+
localIdentifier,
|
|
163
|
+
...(title === undefined ? {} : { title }),
|
|
164
|
+
conditions: source.dashboardMeasureValueFilter.conditions,
|
|
165
|
+
...(dimensionality?.length ? { dimensionality } : {}),
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
if (isDashboardAttributeFilterItem(source) && isDashboardAttributeFilterItem(target)) {
|
|
170
|
+
const targetLocalId = dashboardAttributeFilterItemLocalIdentifier(target);
|
|
171
|
+
if (!targetLocalId) {
|
|
172
|
+
return target;
|
|
173
|
+
}
|
|
174
|
+
return preserveFilterDependencies(target, retargetAttributeFilterItem(source, targetLocalId));
|
|
175
|
+
}
|
|
176
|
+
return target;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* After the active tab commits staged filters and parameters, copy those applied values
|
|
180
|
+
* onto matching filters and parameters on the other tabs. Working overlays are not copied.
|
|
181
|
+
* A target tab's own staged value for a mirrored filter or parameter is dropped, so it cannot
|
|
182
|
+
* shadow the mirrored value; staged values of other filters on that tab are kept.
|
|
183
|
+
*
|
|
184
|
+
* @internal
|
|
185
|
+
*/
|
|
186
|
+
export function mirrorCommittedWorkingSelection(state, sourceTab, stagedFilters, stagedParameterRefs, crossFilteringLocalIdsByTab, enablePersistentFiltersAcrossTabs) {
|
|
187
|
+
if (!enablePersistentFiltersAcrossTabs) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const others = (state.tabs ?? []).filter((tab) => tab.localIdentifier !== sourceTab.localIdentifier);
|
|
191
|
+
if (others.length === 0) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
for (const staged of stagedFilters ?? []) {
|
|
195
|
+
const applied = findAppliedFilterForStaged(sourceTab, staged);
|
|
196
|
+
if (!applied) {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (isCrossFilteringVirtualFilter(sourceTab, dashboardFilterLocalIdentifier(applied), crossFilteringLocalIdsByTab)) {
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
for (const tab of others) {
|
|
203
|
+
const match = findCounterpartFilter(sourceTab, applied, tab, state.attributesWithReferences, {
|
|
204
|
+
allowListToText: false,
|
|
205
|
+
crossFilteringLocalIdsByTab,
|
|
206
|
+
});
|
|
207
|
+
if (!match) {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
writeFilter(tab, match, applyMirroredFilterValue(applied, match));
|
|
211
|
+
dropStagedOverlay(tab, match);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
for (const ref of stagedParameterRefs) {
|
|
215
|
+
const sourceEntry = sourceTab.parameters?.parameters.find((entry) => areObjRefsEqual(entry.parameter.ref, ref));
|
|
216
|
+
if (!sourceEntry) {
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
for (const tab of others) {
|
|
220
|
+
const entry = tab.parameters?.parameters.find((candidate) => areObjRefsEqual(candidate.parameter.ref, ref));
|
|
221
|
+
if (!entry) {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
delete entry.workingOverride;
|
|
225
|
+
entry.runtimeOverride = sourceEntry.runtimeOverride;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Seeds a new tab with the applied filters of `sourceTab` and the configs that belong to them,
|
|
231
|
+
* so every synced filter has a counterpart on the new tab. Cross-filtering virtual filters stay
|
|
232
|
+
* on their own tab. `base` provides the title and description of the new filter context.
|
|
233
|
+
*
|
|
234
|
+
* @internal
|
|
235
|
+
*/
|
|
236
|
+
export function seedSyncedTabFilters(sourceTab, base, crossFilteringLocalIdsByTab) {
|
|
237
|
+
const filters = appliedFilters(sourceTab).filter((filter) => !isCrossFilteringVirtualFilter(sourceTab, dashboardFilterLocalIdentifier(filter), crossFilteringLocalIdsByTab));
|
|
238
|
+
const localIds = new Set(filters.map(dashboardFilterLocalIdentifier).filter((id) => id !== undefined));
|
|
239
|
+
const dateDataSets = filters
|
|
240
|
+
.filter(isDashboardDateFilter)
|
|
241
|
+
.map((filter) => filter.dateFilter.dataSet)
|
|
242
|
+
.filter((ref) => ref !== undefined);
|
|
243
|
+
const groups = (sourceTab.filterGroupsConfig?.groups ?? [])
|
|
244
|
+
.map((group) => ({
|
|
245
|
+
...group,
|
|
246
|
+
filters: group.filters.filter((item) => localIds.has(item.filterLocalIdentifier)),
|
|
247
|
+
}))
|
|
248
|
+
.filter((group) => group.filters.length > 0);
|
|
249
|
+
return {
|
|
250
|
+
filterContextDefinition: { ...base, filters },
|
|
251
|
+
attributeFilterDisplayForms: sourceTab.filterContext?.attributeFilterDisplayForms ?? [],
|
|
252
|
+
attributeFilterConfigs: (sourceTab.attributeFilterConfigs?.attributeFilterConfigs ?? []).filter((config) => localIds.has(config.localIdentifier)),
|
|
253
|
+
dateFilterConfigs: (sourceTab.dateFilterConfigs?.dateFilterConfigs ?? []).filter((config) => dateDataSets.some((ref) => areObjRefsEqual(ref, config.dateDataSet))),
|
|
254
|
+
measureValueFilterConfigs: (sourceTab.measureValueFilterConfigs?.measureValueFilterConfigs ?? []).filter((config) => localIds.has(config.localIdentifier)),
|
|
255
|
+
filterGroupsConfig: groups.length > 0 ? { groups } : undefined,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
function writeFilter(tab, current, next) {
|
|
259
|
+
const filters = tab.filterContext?.filterContextDefinition?.filters;
|
|
260
|
+
if (!filters) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
const index = filters.indexOf(current);
|
|
264
|
+
if (index < 0) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
filters[index] = next;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Removes the working entries that `applyFilterContext` would overlay on `applied`.
|
|
271
|
+
*/
|
|
272
|
+
function dropStagedOverlay(tab, applied) {
|
|
273
|
+
const filterContext = tab.filterContext;
|
|
274
|
+
const working = filterContext?.workingFilterContextDefinition;
|
|
275
|
+
if (!filterContext || !working?.filters.length) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
const overlays = isDashboardDateFilter(applied)
|
|
279
|
+
? (item) => "dateFilter" in item &&
|
|
280
|
+
!!item.dateFilter &&
|
|
281
|
+
(areObjRefsEqual(item.dateFilter.dataSet, applied.dateFilter.dataSet) ||
|
|
282
|
+
(!item.dateFilter.dataSet && !applied.dateFilter.dataSet))
|
|
283
|
+
: (item) => !("dateFilter" in item) &&
|
|
284
|
+
stagedLocalIdentifier(item) !== undefined &&
|
|
285
|
+
stagedLocalIdentifier(item) === dashboardFilterLocalIdentifier(applied);
|
|
286
|
+
const remaining = working.filters.filter((item) => !overlays(item));
|
|
287
|
+
if (remaining.length !== working.filters.length) {
|
|
288
|
+
filterContext.workingFilterContextDefinition = { filters: remaining };
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
function findAppliedFilterForStaged(tab, staged) {
|
|
292
|
+
const filters = appliedFilters(tab);
|
|
293
|
+
if ("dateFilter" in staged && staged.dateFilter) {
|
|
294
|
+
if (!staged.dateFilter.dataSet) {
|
|
295
|
+
return filters.find(isDashboardCommonDateFilter);
|
|
296
|
+
}
|
|
297
|
+
return filters.find((filter) => isDashboardDateFilter(filter) &&
|
|
298
|
+
areObjRefsEqual(filter.dateFilter.dataSet, staged.dateFilter.dataSet));
|
|
299
|
+
}
|
|
300
|
+
const localIdentifier = stagedLocalIdentifier(staged);
|
|
301
|
+
if (!localIdentifier) {
|
|
302
|
+
return undefined;
|
|
303
|
+
}
|
|
304
|
+
return filters.find((filter) => dashboardFilterLocalIdentifier(filter) === localIdentifier);
|
|
305
|
+
}
|
|
306
|
+
function stagedLocalIdentifier(item) {
|
|
307
|
+
if ("attributeFilter" in item && item.attributeFilter) {
|
|
308
|
+
return item.attributeFilter.localIdentifier;
|
|
309
|
+
}
|
|
310
|
+
if ("arbitraryAttributeFilter" in item && item.arbitraryAttributeFilter) {
|
|
311
|
+
return item.arbitraryAttributeFilter.localIdentifier;
|
|
312
|
+
}
|
|
313
|
+
if ("matchAttributeFilter" in item && item.matchAttributeFilter) {
|
|
314
|
+
return item.matchAttributeFilter.localIdentifier;
|
|
315
|
+
}
|
|
316
|
+
if ("dateFilter" in item && item.dateFilter) {
|
|
317
|
+
return item.dateFilter.localIdentifier;
|
|
318
|
+
}
|
|
319
|
+
if ("dashboardMeasureValueFilter" in item && item.dashboardMeasureValueFilter) {
|
|
320
|
+
return item.dashboardMeasureValueFilter.localIdentifier;
|
|
321
|
+
}
|
|
322
|
+
return undefined;
|
|
323
|
+
}
|
|
324
|
+
function appliedFilters(tab) {
|
|
325
|
+
return tab.filterContext?.filterContextDefinition?.filters ?? [];
|
|
326
|
+
}
|
|
327
|
+
function collectDisplayForms(sourceTab, targetTab, attributesWithReferences) {
|
|
328
|
+
return [
|
|
329
|
+
...(sourceTab.filterContext?.attributeFilterDisplayForms ?? []),
|
|
330
|
+
...(targetTab.filterContext?.attributeFilterDisplayForms ?? []),
|
|
331
|
+
...(attributesWithReferences ?? []).flatMap((entry) => entry.attribute.displayForms ?? []),
|
|
332
|
+
];
|
|
333
|
+
}
|
|
334
|
+
function findDateCounterpart(sourceFilter, targets) {
|
|
335
|
+
// Check the data set directly. `isDashboardCommonDateFilter` is typed as
|
|
336
|
+
// `obj is IDashboardDateFilter`, so using it as a guard would narrow the
|
|
337
|
+
// remaining source to `never`.
|
|
338
|
+
const dataSet = sourceFilter.dateFilter.dataSet;
|
|
339
|
+
if (!dataSet) {
|
|
340
|
+
return targets.find(isDashboardCommonDateFilter);
|
|
341
|
+
}
|
|
342
|
+
return targets.find((filter) => isDashboardDateFilter(filter) && areObjRefsEqual(filter.dateFilter.dataSet, dataSet));
|
|
343
|
+
}
|
|
344
|
+
function findMeasureValueCounterpart(sourceFilter, targets) {
|
|
345
|
+
return targets
|
|
346
|
+
.filter(isDashboardMeasureValueFilter)
|
|
347
|
+
.find((filter) => areObjRefsEqual(filter.dashboardMeasureValueFilter.measure, sourceFilter.dashboardMeasureValueFilter.measure) &&
|
|
348
|
+
dimensionalityCompatible(filter.dashboardMeasureValueFilter.dimensionality, sourceFilter.dashboardMeasureValueFilter.dimensionality));
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* A measure value filter syncs only with a filter on the same measure and the same dimensionality.
|
|
352
|
+
* Missing dimensionality matches only another missing dimensionality.
|
|
353
|
+
*/
|
|
354
|
+
function dimensionalityCompatible(left, right) {
|
|
355
|
+
const leftRefs = left ?? [];
|
|
356
|
+
const rightRefs = right ?? [];
|
|
357
|
+
if (leftRefs.length !== rightRefs.length) {
|
|
358
|
+
return false;
|
|
359
|
+
}
|
|
360
|
+
return leftRefs.every((ref, index) => areObjRefsEqual(ref, rightRefs[index]));
|
|
361
|
+
}
|
|
@@ -137,6 +137,8 @@ export declare const tabsActions: import("@reduxjs/toolkit").CaseReducerActions<
|
|
|
137
137
|
payload: {
|
|
138
138
|
readonly localIdentifier: string;
|
|
139
139
|
readonly dimensionality?: import("@gooddata/sdk-model").ObjRef[];
|
|
140
|
+
readonly restoreTargetOriginal?: boolean;
|
|
141
|
+
readonly enablePersistentFiltersAcrossTabs?: boolean;
|
|
140
142
|
};
|
|
141
143
|
type: string;
|
|
142
144
|
}) => void | import("./tabsState.js").ITabsState | import("immer").WritableDraft<import("./tabsState.js").ITabsState>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/model/store/tabs/index.ts"],"names":[],"mappings":"AA+BA,eAAO,MAAM,gBAAgB,8DAAoB,CAAC;AAElD;;GAEG;AACH,eAAO,MAAM,WAAW
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/model/store/tabs/index.ts"],"names":[],"mappings":"AA+BA,eAAO,MAAM,gBAAgB,8DAAoB,CAAC;AAElD;;GAEG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAAoB,CAAC"}
|
|
@@ -20,8 +20,13 @@ export interface ISetParameterRuntimeValuesPayload {
|
|
|
20
20
|
values: IParameterValueChange[];
|
|
21
21
|
/**
|
|
22
22
|
* Target tab. When omitted, the active tab is used.
|
|
23
|
+
* With `enablePersistentFiltersAcrossTabs`, this tab is the source and every other tab is written too.
|
|
23
24
|
*/
|
|
24
25
|
tabLocalIdentifier?: string;
|
|
26
|
+
/**
|
|
27
|
+
* When true, write the values onto every tab. A tab that does not define the parameter is unchanged.
|
|
28
|
+
*/
|
|
29
|
+
enablePersistentFiltersAcrossTabs?: boolean;
|
|
25
30
|
}
|
|
26
31
|
/**
|
|
27
32
|
* Stage a parameter value under the apply-all-at-once mode. The value is always concrete: the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parametersReducers.d.ts","sourceRoot":"","sources":["../../../../../src/model/store/tabs/parameters/parametersReducers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,WAAW,EAAsB,MAAM,kBAAkB,CAAC;AAErF,OAAO,EACH,KAAK,mBAAmB,EACxB,KAAK,MAAM,EACX,KAAK,cAAc,EAEtB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,
|
|
1
|
+
{"version":3,"file":"parametersReducers.d.ts","sourceRoot":"","sources":["../../../../../src/model/store/tabs/parameters/parametersReducers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,WAAW,EAAsB,MAAM,kBAAkB,CAAC;AAErF,OAAO,EACH,KAAK,mBAAmB,EACxB,KAAK,MAAM,EACX,KAAK,cAAc,EAEtB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,EACH,KAAK,SAAS,EACd,KAAK,UAAU,EAIlB,MAAM,iBAAiB,CAAC;AAIzB,KAAK,iBAAiB,CAAC,CAAC,SAAS,MAAM,IAAI,WAAW,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;AAEtE;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACjC,SAAS,EAAE,mBAAmB,CAAC;IAC/B,gBAAgB,EAAE,cAAc,CAAC;CACpC;AAoBD;;GAEG;AACH,MAAM,WAAW,iCAAiC;IAC9C,MAAM,EAAE,qBAAqB,EAAE,CAAC;IAChC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;OAEG;IACH,iCAAiC,CAAC,EAAE,OAAO,CAAC;CAC/C;AAmBD;;;;;;GAMG;AACH,MAAM,WAAW,gCAAgC;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,cAAc,CAAC;CACzB;AAkBD;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACpC,GAAG,EAAE,MAAM,CAAC;CACf;AAoCD;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,CAOjE;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,CAIhE;AAED,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;CAK9B,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// (C) 2026 GoodData Corporation
|
|
2
2
|
import { areObjRefsEqual, } from "@gooddata/sdk-model";
|
|
3
|
-
import { getActiveTab, getTabOrActive } from "../tabsState.js";
|
|
3
|
+
import { getActiveTab, getTabOrActive, getTabsToMutate, } from "../tabsState.js";
|
|
4
4
|
import { parametersInitialState } from "./parametersState.js";
|
|
5
5
|
const addParameter = (state, action) => {
|
|
6
6
|
const activeTab = getActiveTab(state);
|
|
@@ -20,8 +20,12 @@ const addParameter = (state, action) => {
|
|
|
20
20
|
};
|
|
21
21
|
};
|
|
22
22
|
const setParameterRuntimeValues = (state, action) => {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
// Parameter values sync by parameter ref. A tab that does not define the parameter is a no-op.
|
|
24
|
+
// Working overrides are not written here.
|
|
25
|
+
for (const tab of getTabsToMutate(state, action.payload.tabLocalIdentifier, action.payload.enablePersistentFiltersAcrossTabs)) {
|
|
26
|
+
for (const entry of action.payload.values) {
|
|
27
|
+
setRuntimeOverrideOnTab(tab, entry);
|
|
28
|
+
}
|
|
25
29
|
}
|
|
26
30
|
};
|
|
27
31
|
const setParameterWorkingValue = (state, action) => {
|
|
@@ -46,8 +50,8 @@ const removeParameter = (state, action) => {
|
|
|
46
50
|
parameters: activeTab.parameters.parameters.filter((entry) => !areObjRefsEqual(entry.parameter.ref, action.payload.ref)),
|
|
47
51
|
};
|
|
48
52
|
};
|
|
49
|
-
function
|
|
50
|
-
const entry =
|
|
53
|
+
function setRuntimeOverrideOnTab(tab, { ref, value }) {
|
|
54
|
+
const entry = tab.parameters?.parameters.find((candidate) => areObjRefsEqual(candidate.parameter.ref, ref));
|
|
51
55
|
if (!entry) {
|
|
52
56
|
return;
|
|
53
57
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Action, type CaseReducer } from "@reduxjs/toolkit";
|
|
2
2
|
import { type ObjRef } from "@gooddata/sdk-model";
|
|
3
|
+
import { type CrossFilteringLocalIdsByTab } from "./filterContext/filterSync.js";
|
|
3
4
|
import { type ITabState, type ITabsState } from "./tabsState.js";
|
|
4
5
|
/**
|
|
5
6
|
* @alpha
|
|
@@ -34,6 +35,11 @@ export interface ISetDashboardAttributeFilterConfigDisplayAsLabelPayload {
|
|
|
34
35
|
*/
|
|
35
36
|
export interface IApplyWorkingSelectionPayload {
|
|
36
37
|
readonly enableImmediateAttributeFilterDisplayAsLabelMigration?: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Cross-filtering virtual filters per tab. They are not mirrored to or from other tabs.
|
|
40
|
+
*/
|
|
41
|
+
readonly crossFilteringLocalIdsByTab?: CrossFilteringLocalIdsByTab;
|
|
42
|
+
readonly enablePersistentFiltersAcrossTabs?: boolean;
|
|
37
43
|
}
|
|
38
44
|
export declare const tabsReducers: {
|
|
39
45
|
setTabs: TabsReducer<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tabsReducers.d.ts","sourceRoot":"","sources":["../../../../src/model/store/tabs/tabsReducers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,WAAW,
|
|
1
|
+
{"version":3,"file":"tabsReducers.d.ts","sourceRoot":"","sources":["../../../../src/model/store/tabs/tabsReducers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,WAAW,EAA+B,MAAM,kBAAkB,CAAC;AAE9F,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAOlD,OAAO,EACH,KAAK,2BAA2B,EAEnC,MAAM,+BAA+B,CAAC;AAKvC,OAAO,EAEH,KAAK,SAAS,EACd,KAAK,UAAU,EAGlB,MAAM,gBAAgB,CAAC;AAExB;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,MAAM,IAAI,WAAW,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;AAEvE,KAAK,cAAc,GAAG;IAClB;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC;IAEnB;;OAEG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACrC,CAAC;AA0FF;;;GAGG;AACH,MAAM,WAAW,uDAAuD;IACpE;;OAEG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB;;OAEG;IACH,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;CACtC;AAkDD;;GAEG;AACH,MAAM,WAAW,6BAA6B;IAC1C,QAAQ,CAAC,qDAAqD,CAAC,EAAE,OAAO,CAAC;IACzE;;OAEG;IACH,QAAQ,CAAC,2BAA2B,CAAC,EAAE,2BAA2B,CAAC;IACnE,QAAQ,CAAC,iCAAiC,CAAC,EAAE,OAAO,CAAC;CACxD;AAsCD,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;mBA5FkC,MAAM;wBAAc,OAAO;;;;;;mBAkBlC,MAAM;mBAAS,MAAM;;;;;;gCAeA,MAAM;;;;;;;;;;;;CAsE9E,CAAC"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// (C) 2021-2026 GoodData Corporation
|
|
2
|
+
import { current } from "@reduxjs/toolkit";
|
|
2
3
|
import { clearFilterContextWorkingValues, commitFilterContextWorkingValues, } from "./filterContext/filterContextReducers.js";
|
|
3
4
|
import { initializeFilterContext } from "./filterContext/filterContextUtils.js";
|
|
5
|
+
import { mirrorCommittedWorkingSelection, } from "./filterContext/filterSync.js";
|
|
4
6
|
import { clearParameterWorkingValues, commitParameterWorkingValues, } from "./parameters/parametersReducers.js";
|
|
5
|
-
import { DEFAULT_TAB_ID, getActiveTab } from "./tabsState.js";
|
|
7
|
+
import { DEFAULT_TAB_ID, getActiveTab, getTabsToMutate, } from "./tabsState.js";
|
|
6
8
|
/**
|
|
7
9
|
* Applies initialization logic to a tab's filterContext if present.
|
|
8
10
|
* Ensures filters have local identifiers and proper ordering.
|
|
@@ -119,16 +121,22 @@ const applyWorkingSelection = (state, action) => {
|
|
|
119
121
|
if (!activeTab) {
|
|
120
122
|
return;
|
|
121
123
|
}
|
|
124
|
+
const stagedFilters = activeTab.filterContext?.workingFilterContextDefinition
|
|
125
|
+
? current(activeTab.filterContext.workingFilterContextDefinition).filters
|
|
126
|
+
: undefined;
|
|
127
|
+
const stagedParameterRefs = (activeTab.parameters?.parameters ?? [])
|
|
128
|
+
.filter((entry) => entry.workingOverride !== undefined)
|
|
129
|
+
.map((entry) => entry.parameter.ref);
|
|
122
130
|
commitFilterContextWorkingValues(activeTab, action.payload.enableImmediateAttributeFilterDisplayAsLabelMigration);
|
|
123
131
|
commitParameterWorkingValues(activeTab);
|
|
132
|
+
// Mirror the committed applied values, not the working overlay.
|
|
133
|
+
mirrorCommittedWorkingSelection(state, activeTab, stagedFilters, stagedParameterRefs, action.payload.crossFilteringLocalIdsByTab, action.payload.enablePersistentFiltersAcrossTabs);
|
|
124
134
|
};
|
|
125
135
|
const resetWorkingSelection = (state) => {
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
136
|
+
for (const tab of getTabsToMutate(state)) {
|
|
137
|
+
clearFilterContextWorkingValues(tab);
|
|
138
|
+
clearParameterWorkingValues(tab);
|
|
129
139
|
}
|
|
130
|
-
clearFilterContextWorkingValues(activeTab);
|
|
131
|
-
clearParameterWorkingValues(activeTab);
|
|
132
140
|
};
|
|
133
141
|
export const tabsReducers = {
|
|
134
142
|
setTabs,
|
|
@@ -24,6 +24,16 @@ export declare const getActiveTab: (state: ITabsState) => ITabState | undefined;
|
|
|
24
24
|
* @internal
|
|
25
25
|
*/
|
|
26
26
|
export declare const getTabOrActive: (state: ITabsState, tabLocalIdentifier?: string) => ITabState | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Tabs a filter or parameter mutation should write.
|
|
29
|
+
*
|
|
30
|
+
* With `persistFiltersAcrossTabs` off, that is the tab addressed by `tabLocalIdentifier`
|
|
31
|
+
* or the active tab. With the flag on, that is every tab, source first. Working-selection
|
|
32
|
+
* edits stay on the source tab so half-applied values are not mirrored.
|
|
33
|
+
*
|
|
34
|
+
* @internal
|
|
35
|
+
*/
|
|
36
|
+
export declare const getTabsToMutate: (state: ITabsState, tabLocalIdentifier?: string, enablePersistentFiltersAcrossTabs?: boolean, isWorkingSelectionChange?: boolean) => ITabState[];
|
|
27
37
|
/**
|
|
28
38
|
* @alpha
|
|
29
39
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tabsState.d.ts","sourceRoot":"","sources":["../../../../src/model/store/tabs/tabsState.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAC1E,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AAExE,OAAO,EAAE,KAAK,4BAA4B,EAAE,MAAM,0DAA0D,CAAC;AAC7G,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6CAA6C,CAAC;AAC1F,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,+CAA+C,CAAC;AAC7F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,KAAK,+BAA+B,EAAE,MAAM,+DAA+D,CAAC;AACrH,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAExE;;;;GAIG;AACH,eAAO,MAAM,cAAc,iBAAiB,CAAC;AAE7C,eAAO,MAAM,YAAY,UAAW,UAAU,KAAG,SAAS,GAAG,SAK5D,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,UAAW,UAAU,uBAAuB,MAAM,KAAG,SAAS,GAAG,SAQ3F,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,SAAS;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IAExB,gBAAgB,CAAC,EAAE,sBAAsB,CAAC;IAC1C,iBAAiB,CAAC,EAAE,uBAAuB,CAAC;IAC5C,sBAAsB,CAAC,EAAE,4BAA4B,CAAC;IACtD,yBAAyB,CAAC,EAAE,+BAA+B,CAAC;IAC5D,aAAa,CAAC,EAAE,kBAAkB,CAAC;IACnC,kBAAkB,CAAC,EAAE,4BAA4B,CAAC;IAClD,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAE9B;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACvB;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC;IACnB;;OAEG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC;;;OAGG;IACH,wBAAwB,CAAC,EAAE,wBAAwB,EAAE,CAAC;CACzD;AAED,eAAO,MAAM,gBAAgB,EAAE,UAI9B,CAAC"}
|
|
1
|
+
{"version":3,"file":"tabsState.d.ts","sourceRoot":"","sources":["../../../../src/model/store/tabs/tabsState.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAC1E,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AAExE,OAAO,EAAE,KAAK,4BAA4B,EAAE,MAAM,0DAA0D,CAAC;AAC7G,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,6CAA6C,CAAC;AAC1F,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,+CAA+C,CAAC;AAC7F,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAChF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,KAAK,+BAA+B,EAAE,MAAM,+DAA+D,CAAC;AACrH,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAExE;;;;GAIG;AACH,eAAO,MAAM,cAAc,iBAAiB,CAAC;AAE7C,eAAO,MAAM,YAAY,UAAW,UAAU,KAAG,SAAS,GAAG,SAK5D,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,UAAW,UAAU,uBAAuB,MAAM,KAAG,SAAS,GAAG,SAQ3F,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,UACjB,UAAU,uBACI,MAAM,sCACS,OAAO,6BAChB,OAAO,KACnC,SAAS,EAUX,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,SAAS;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IAExB,gBAAgB,CAAC,EAAE,sBAAsB,CAAC;IAC1C,iBAAiB,CAAC,EAAE,uBAAuB,CAAC;IAC5C,sBAAsB,CAAC,EAAE,4BAA4B,CAAC;IACtD,yBAAyB,CAAC,EAAE,+BAA+B,CAAC;IAC5D,aAAa,CAAC,EAAE,kBAAkB,CAAC;IACnC,kBAAkB,CAAC,EAAE,4BAA4B,CAAC;IAClD,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAE9B;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACvB;;OAEG;IACH,IAAI,CAAC,EAAE,SAAS,EAAE,CAAC;IACnB;;OAEG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC;;;OAGG;IACH,wBAAwB,CAAC,EAAE,wBAAwB,EAAE,CAAC;CACzD;AAED,eAAO,MAAM,gBAAgB,EAAE,UAI9B,CAAC"}
|
|
@@ -29,6 +29,26 @@ export const getTabOrActive = (state, tabLocalIdentifier) => {
|
|
|
29
29
|
}
|
|
30
30
|
return getActiveTab(state);
|
|
31
31
|
};
|
|
32
|
+
/**
|
|
33
|
+
* Tabs a filter or parameter mutation should write.
|
|
34
|
+
*
|
|
35
|
+
* With `persistFiltersAcrossTabs` off, that is the tab addressed by `tabLocalIdentifier`
|
|
36
|
+
* or the active tab. With the flag on, that is every tab, source first. Working-selection
|
|
37
|
+
* edits stay on the source tab so half-applied values are not mirrored.
|
|
38
|
+
*
|
|
39
|
+
* @internal
|
|
40
|
+
*/
|
|
41
|
+
export const getTabsToMutate = (state, tabLocalIdentifier, enablePersistentFiltersAcrossTabs, isWorkingSelectionChange) => {
|
|
42
|
+
const source = getTabOrActive(state, tabLocalIdentifier);
|
|
43
|
+
if (!source) {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
if (isWorkingSelectionChange || !enablePersistentFiltersAcrossTabs) {
|
|
47
|
+
return [source];
|
|
48
|
+
}
|
|
49
|
+
const others = (state.tabs ?? []).filter((tab) => tab.localIdentifier !== source.localIdentifier);
|
|
50
|
+
return [source, ...others];
|
|
51
|
+
};
|
|
32
52
|
export const tabsInitialState = {
|
|
33
53
|
tabs: undefined,
|
|
34
54
|
activeTabLocalIdentifier: undefined,
|
|
@@ -18,9 +18,11 @@ export interface IDashboardCopySourceTab {
|
|
|
18
18
|
*
|
|
19
19
|
* @remarks
|
|
20
20
|
* The backend accepts a copy that keeps a drill to a dashboard the user cannot open or an ignored
|
|
21
|
-
* drill-down hierarchy, so neither counts. It refuses a copy whose filters use a label, metric
|
|
22
|
-
* the user may not read: an attribute filter's label, a measure value filter's metric
|
|
23
|
-
* fact limiting a filter's values. A
|
|
21
|
+
* drill-down hierarchy, so neither counts. It refuses a copy whose filters use a label, attribute, metric
|
|
22
|
+
* or fact the user may not read: an attribute filter's label, a measure value filter's metric or
|
|
23
|
+
* "For attribute" item, or a metric, fact or "Count of" attribute limiting a filter's values. A filter
|
|
24
|
+
* restricted only through its parent filter does not count, as it names the parent by local identifier
|
|
25
|
+
* only. A merely deleted object does not count.
|
|
24
26
|
*
|
|
25
27
|
* @internal
|
|
26
28
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"copyBlockingReferences.d.ts","sourceRoot":"","sources":["../../../../src/model/store/unavailableObjects/copyBlockingReferences.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,2BAA2B,CAAC;AAChF,OAAO,EACH,KAAK,iBAAiB,EAEtB,KAAK,+BAA+B,EACpC,KAAK,gBAAgB,EAErB,KAAK,QAAQ,
|
|
1
|
+
{"version":3,"file":"copyBlockingReferences.d.ts","sourceRoot":"","sources":["../../../../src/model/store/unavailableObjects/copyBlockingReferences.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,2BAA2B,CAAC;AAChF,OAAO,EACH,KAAK,iBAAiB,EAEtB,KAAK,+BAA+B,EACpC,KAAK,gBAAgB,EAErB,KAAK,QAAQ,EAchB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,KAAK,uBAAuB,EAAmC,MAAM,4BAA4B,CAAC;AAQ3G,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAKrD;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACpC,MAAM,CAAC,EAAE,gBAAgB,CAAC,uBAAuB,CAAC,CAAC;IACnD,OAAO,EAAE,iBAAiB,EAAE,CAAC;IAC7B,sBAAsB,EAAE,+BAA+B,EAAE,CAAC;CAC7D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CACpC,IAAI,EAAE,uBAAuB,EAAE,EAC/B,QAAQ,EAAE,QAAQ,EAAE,EACpB,kBAAkB,EAAE,8BAA8B,EAAE,GACrD,OAAO,CAgET;AAED;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B,EAAE,iBAAiB,CAAC,OAAO,CAqBtE,CAAC"}
|
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
// (C) 2026 GoodData Corporation
|
|
2
2
|
import { createSelector } from "@reduxjs/toolkit";
|
|
3
|
-
import { areObjRefsEqual,
|
|
3
|
+
import { areObjRefsEqual, insightRef, isComputedAttributeRef, isDashboardAttributeFilterReference, isDashboardMeasureValueFilterReference, isDrillToDashboard, isInsightWidget, isKpiWidget, isRichTextWidget, isVisualizationSwitcherWidget, } from "@gooddata/sdk-model";
|
|
4
4
|
import { collectReferences } from "@gooddata/sdk-ui-kit";
|
|
5
5
|
import { isExtendedDashboardLayoutWidget } from "../../types/layoutTypes.js";
|
|
6
|
-
import { isDashboardObjectRestricted } from "../filtering/restrictedFilterUtils.js";
|
|
6
|
+
import { isDashboardFilterUsingRestrictedObject, isDashboardObjectRestricted, } from "../filtering/restrictedFilterUtils.js";
|
|
7
7
|
import { selectInsights } from "../insights/insightsSelectors.js";
|
|
8
8
|
import { selectIsInViewMode } from "../renderMode/renderModeSelectors.js";
|
|
9
9
|
import { selectTabs } from "../tabs/tabsSelectors.js";
|
|
10
10
|
import { isDrillRestricted } from "../widgetDrills/drillRestrictionUtils.js";
|
|
11
|
-
import {
|
|
11
|
+
import { selectUnavailableObjects } from "./unavailableObjectsSelectors.js";
|
|
12
12
|
/**
|
|
13
13
|
* Tells whether a copy of the dashboard would reference an object the current user may not read in a
|
|
14
14
|
* place the backend checks when it creates the copy, so it would refuse the copy.
|
|
15
15
|
*
|
|
16
16
|
* @remarks
|
|
17
17
|
* The backend accepts a copy that keeps a drill to a dashboard the user cannot open or an ignored
|
|
18
|
-
* drill-down hierarchy, so neither counts. It refuses a copy whose filters use a label, metric
|
|
19
|
-
* the user may not read: an attribute filter's label, a measure value filter's metric
|
|
20
|
-
* fact limiting a filter's values. A
|
|
18
|
+
* drill-down hierarchy, so neither counts. It refuses a copy whose filters use a label, attribute, metric
|
|
19
|
+
* or fact the user may not read: an attribute filter's label, a measure value filter's metric or
|
|
20
|
+
* "For attribute" item, or a metric, fact or "Count of" attribute limiting a filter's values. A filter
|
|
21
|
+
* restricted only through its parent filter does not count, as it names the parent by local identifier
|
|
22
|
+
* only. A merely deleted object does not count.
|
|
21
23
|
*
|
|
22
24
|
* @internal
|
|
23
25
|
*/
|
|
@@ -55,20 +57,11 @@ export function hasCopyBlockingReference(tabs, insights, unavailableObjects) {
|
|
|
55
57
|
}
|
|
56
58
|
return false;
|
|
57
59
|
};
|
|
58
|
-
const restrictedLimitingItems = newRestrictedLimitingItemsMap(unavailableObjects);
|
|
59
|
-
const isFilterForbidden = (filter) => {
|
|
60
|
-
if (isDashboardMeasureValueFilter(filter)) {
|
|
61
|
-
return isForbidden(filter.dashboardMeasureValueFilter.measure, "measure");
|
|
62
|
-
}
|
|
63
|
-
return (isDashboardAttributeFilterItem(filter) &&
|
|
64
|
-
(isLabelForbidden(dashboardAttributeFilterItemDisplayForm(filter)) ||
|
|
65
|
-
(dashboardAttributeFilterItemValidateElementsBy(filter) ?? []).some((item) => restrictedLimitingItems.has(item))));
|
|
66
|
-
};
|
|
67
60
|
const isSectionForbidden = (section) => isTextForbidden(section.header?.description) ||
|
|
68
61
|
section.items.some((item) => item.widget !== undefined && isWidgetForbidden(item.widget));
|
|
69
62
|
return tabs.some((tab) => (tab.layout?.sections ?? []).some(isSectionForbidden) ||
|
|
70
63
|
tab.attributeFilterConfigs.some((config) => isLabelForbidden(config.displayAsLabel)) ||
|
|
71
|
-
tab.filters.some(
|
|
64
|
+
tab.filters.some((filter) => isDashboardFilterUsingRestrictedObject(filter, unavailableObjects)));
|
|
72
65
|
}
|
|
73
66
|
/**
|
|
74
67
|
* Selects whether "Save as new" would fail because the dashboard, as it stands now, references objects
|