@gooddata/sdk-ui-dashboard 11.65.0-alpha.1 → 11.65.0-alpha.11

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 (426) hide show
  1. package/NOTICE +72039 -4
  2. package/esm/__version.d.ts +1 -1
  3. package/esm/__version.d.ts.map +1 -1
  4. package/esm/__version.js +1 -1
  5. package/esm/assets/type-csv-formatted.svg +1 -1
  6. package/esm/assets/type-csv-raw.svg +1 -1
  7. package/esm/index.d.ts +8 -5
  8. package/esm/index.d.ts.map +1 -1
  9. package/esm/index.js +3 -2
  10. package/esm/model/commandHandlers/dashboard/common/filterViews.d.ts +36 -3
  11. package/esm/model/commandHandlers/dashboard/common/filterViews.d.ts.map +1 -1
  12. package/esm/model/commandHandlers/dashboard/common/filterViews.js +138 -26
  13. package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/index.d.ts +15 -4
  14. package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/index.d.ts.map +1 -1
  15. package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/index.js +55 -11
  16. package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/preloadAttributeFiltersData.d.ts +6 -1
  17. package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/preloadAttributeFiltersData.d.ts.map +1 -1
  18. package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/preloadAttributeFiltersData.js +14 -6
  19. package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/resolveDashboardConfig.d.ts.map +1 -1
  20. package/esm/model/commandHandlers/dashboard/initializeDashboardHandler/resolveDashboardConfig.js +1 -3
  21. package/esm/model/commandHandlers/dashboard/resetDashboardHandler.d.ts.map +1 -1
  22. package/esm/model/commandHandlers/dashboard/resetDashboardHandler.js +21 -8
  23. package/esm/model/commandHandlers/filterContext/attributeFilter/changeAttributeDisplayFormHandler.d.ts.map +1 -1
  24. package/esm/model/commandHandlers/filterContext/attributeFilter/changeAttributeDisplayFormHandler.js +8 -7
  25. package/esm/model/commandHandlers/filterContext/attributeFilter/changeAttributeFilterSelectionHandler.d.ts.map +1 -1
  26. package/esm/model/commandHandlers/filterContext/attributeFilter/changeAttributeFilterSelectionHandler.js +19 -28
  27. package/esm/model/commandHandlers/filterContext/attributeFilter/removeAttributeFiltersHandler.d.ts.map +1 -1
  28. package/esm/model/commandHandlers/filterContext/attributeFilter/removeAttributeFiltersHandler.js +4 -5
  29. package/esm/model/commandHandlers/filterContext/attributeFilter/replaceAttributeFilterItemSelectionHandler.d.ts.map +1 -1
  30. package/esm/model/commandHandlers/filterContext/attributeFilter/replaceAttributeFilterItemSelectionHandler.js +14 -15
  31. package/esm/model/commandHandlers/filterContext/attributeFilter/resolveDisplayFormMetadata.d.ts +4 -1
  32. package/esm/model/commandHandlers/filterContext/attributeFilter/resolveDisplayFormMetadata.d.ts.map +1 -1
  33. package/esm/model/commandHandlers/filterContext/attributeFilter/resolveDisplayFormMetadata.js +11 -2
  34. package/esm/model/commandHandlers/filterContext/changeFilterContextSelectionHandler.d.ts.map +1 -1
  35. package/esm/model/commandHandlers/filterContext/changeFilterContextSelectionHandler.js +28 -44
  36. package/esm/model/commandHandlers/filterContext/common.d.ts +6 -5
  37. package/esm/model/commandHandlers/filterContext/common.d.ts.map +1 -1
  38. package/esm/model/commandHandlers/filterContext/common.js +12 -18
  39. package/esm/model/commandHandlers/filterContext/dateFilter/changeDateFilterSelectionHandler.d.ts.map +1 -1
  40. package/esm/model/commandHandlers/filterContext/dateFilter/changeDateFilterSelectionHandler.js +30 -36
  41. package/esm/model/commandHandlers/filterContext/filterViewHandler.d.ts +10 -0
  42. package/esm/model/commandHandlers/filterContext/filterViewHandler.d.ts.map +1 -1
  43. package/esm/model/commandHandlers/filterContext/filterViewHandler.js +79 -24
  44. package/esm/model/commandHandlers/filterContext/measureValueFilter/changeMeasureValueFilterConditionHandler.d.ts.map +1 -1
  45. package/esm/model/commandHandlers/filterContext/measureValueFilter/changeMeasureValueFilterConditionHandler.js +11 -17
  46. package/esm/model/commandHandlers/filterContext/measureValueFilter/changeMeasureValueFilterDimensionalityHandler.d.ts.map +1 -1
  47. package/esm/model/commandHandlers/filterContext/measureValueFilter/changeMeasureValueFilterDimensionalityHandler.js +7 -10
  48. package/esm/model/commandHandlers/filterContext/tabSync/expandActionsForTabSync.d.ts +15 -0
  49. package/esm/model/commandHandlers/filterContext/tabSync/expandActionsForTabSync.d.ts.map +1 -0
  50. package/esm/model/commandHandlers/filterContext/tabSync/expandActionsForTabSync.js +88 -0
  51. package/esm/model/commandHandlers/filterContext/tabSync/tabSyncRules.d.ts +11 -0
  52. package/esm/model/commandHandlers/filterContext/tabSync/tabSyncRules.d.ts.map +1 -0
  53. package/esm/model/commandHandlers/filterContext/tabSync/tabSyncRules.js +335 -0
  54. package/esm/model/commandHandlers/filterContext/tabSync/tabSyncSaga.d.ts +52 -0
  55. package/esm/model/commandHandlers/filterContext/tabSync/tabSyncSaga.d.ts.map +1 -0
  56. package/esm/model/commandHandlers/filterContext/tabSync/tabSyncSaga.js +60 -0
  57. package/esm/model/commandHandlers/filterContext/tabSync/tabSyncScope.d.ts +69 -0
  58. package/esm/model/commandHandlers/filterContext/tabSync/tabSyncScope.d.ts.map +1 -0
  59. package/esm/model/commandHandlers/filterContext/tabSync/tabSyncScope.js +2 -0
  60. package/esm/model/commandHandlers/filterContext/tabSync/writtenTargets.d.ts +17 -0
  61. package/esm/model/commandHandlers/filterContext/tabSync/writtenTargets.d.ts.map +1 -0
  62. package/esm/model/commandHandlers/filterContext/tabSync/writtenTargets.js +79 -0
  63. package/esm/model/commandHandlers/parameters/changeParameterValuesHandler.d.ts +2 -1
  64. package/esm/model/commandHandlers/parameters/changeParameterValuesHandler.d.ts.map +1 -1
  65. package/esm/model/commandHandlers/parameters/changeParameterValuesHandler.js +44 -9
  66. package/esm/model/commandHandlers/tabs/switchDashboardTabHandler.d.ts.map +1 -1
  67. package/esm/model/commandHandlers/tabs/switchDashboardTabHandler.js +3 -1
  68. package/esm/model/commands/filters.d.ts +16 -1
  69. package/esm/model/commands/filters.d.ts.map +1 -1
  70. package/esm/model/commands/filters.js +2 -1
  71. package/esm/model/commands/parameters.d.ts +20 -1
  72. package/esm/model/commands/parameters.d.ts.map +1 -1
  73. package/esm/model/commands/parameters.js +3 -1
  74. package/esm/model/events/base.d.ts +1 -1
  75. package/esm/model/events/base.d.ts.map +1 -1
  76. package/esm/model/events/index.d.ts +2 -1
  77. package/esm/model/events/index.d.ts.map +1 -1
  78. package/esm/model/events/insightEditor.d.ts +128 -0
  79. package/esm/model/events/insightEditor.d.ts.map +1 -0
  80. package/esm/model/events/insightEditor.js +74 -0
  81. package/esm/model/react/useInitializeDashboardStore.d.ts.map +1 -1
  82. package/esm/model/react/useInitializeDashboardStore.js +4 -9
  83. package/esm/model/store/config/configReducers.d.ts +4 -8
  84. package/esm/model/store/config/configReducers.d.ts.map +1 -1
  85. package/esm/model/store/config/configReducers.js +6 -12
  86. package/esm/model/store/config/configSelectors.d.ts +24 -0
  87. package/esm/model/store/config/configSelectors.d.ts.map +1 -1
  88. package/esm/model/store/config/configSelectors.js +30 -4
  89. package/esm/model/store/config/index.d.ts +2 -3
  90. package/esm/model/store/config/index.d.ts.map +1 -1
  91. package/esm/model/store/filterViews/filterViewsReducers.d.ts +8 -0
  92. package/esm/model/store/filterViews/filterViewsReducers.d.ts.map +1 -1
  93. package/esm/model/store/filterViews/filterViewsReducers.js +11 -0
  94. package/esm/model/store/filterViews/filterViewsReducersSelectors.d.ts +6 -0
  95. package/esm/model/store/filterViews/filterViewsReducersSelectors.d.ts.map +1 -1
  96. package/esm/model/store/filterViews/filterViewsReducersSelectors.js +6 -0
  97. package/esm/model/store/filterViews/filterViewsState.d.ts +6 -0
  98. package/esm/model/store/filterViews/filterViewsState.d.ts.map +1 -1
  99. package/esm/model/store/filterViews/index.d.ts +2 -0
  100. package/esm/model/store/filterViews/index.d.ts.map +1 -1
  101. package/esm/model/store/meta/metaSelectors.d.ts +7 -0
  102. package/esm/model/store/meta/metaSelectors.d.ts.map +1 -1
  103. package/esm/model/store/meta/metaSelectors.js +10 -0
  104. package/esm/model/store/tabs/attributeFilterConfigs/attributeFilterConfigsReducers.d.ts +75 -5
  105. package/esm/model/store/tabs/attributeFilterConfigs/attributeFilterConfigsReducers.d.ts.map +1 -1
  106. package/esm/model/store/tabs/attributeFilterConfigs/attributeFilterConfigsReducers.js +35 -60
  107. package/esm/model/store/tabs/dateFilterConfigs/dateFilterConfigsReducers.js +1 -1
  108. package/esm/model/store/tabs/filterContext/filterContextReducers.d.ts +163 -31
  109. package/esm/model/store/tabs/filterContext/filterContextReducers.d.ts.map +1 -1
  110. package/esm/model/store/tabs/filterContext/filterContextReducers.js +389 -529
  111. package/esm/model/store/tabs/filterContext/filterSync.d.ts +35 -13
  112. package/esm/model/store/tabs/filterContext/filterSync.d.ts.map +1 -1
  113. package/esm/model/store/tabs/filterContext/filterSync.js +79 -58
  114. package/esm/model/store/tabs/index.d.ts +18 -12
  115. package/esm/model/store/tabs/index.d.ts.map +1 -1
  116. package/esm/model/store/tabs/layout/layoutReducers.d.ts +1 -0
  117. package/esm/model/store/tabs/layout/layoutReducers.d.ts.map +1 -1
  118. package/esm/model/store/tabs/measureValueFilterConfigs/measureValueFilterConfigsReducers.d.ts.map +1 -1
  119. package/esm/model/store/tabs/parameters/parametersReducers.d.ts +16 -5
  120. package/esm/model/store/tabs/parameters/parametersReducers.d.ts.map +1 -1
  121. package/esm/model/store/tabs/parameters/parametersReducers.js +33 -7
  122. package/esm/model/store/tabs/parameters/parametersSelectors.d.ts +7 -0
  123. package/esm/model/store/tabs/parameters/parametersSelectors.d.ts.map +1 -1
  124. package/esm/model/store/tabs/parameters/parametersSelectors.js +16 -0
  125. package/esm/model/store/tabs/tabsReducers.d.ts +8 -4
  126. package/esm/model/store/tabs/tabsReducers.d.ts.map +1 -1
  127. package/esm/model/store/tabs/tabsReducers.js +15 -18
  128. package/esm/model/store/tabs/tabsState.d.ts +0 -10
  129. package/esm/model/store/tabs/tabsState.d.ts.map +1 -1
  130. package/esm/model/store/tabs/tabsState.js +0 -20
  131. package/esm/model/store/ui/index.d.ts +2 -0
  132. package/esm/model/store/ui/index.d.ts.map +1 -1
  133. package/esm/model/store/ui/uiReducers.d.ts +6 -0
  134. package/esm/model/store/ui/uiReducers.d.ts.map +1 -1
  135. package/esm/model/store/ui/uiReducers.js +8 -0
  136. package/esm/model/store/ui/uiSelectors.d.ts +7 -0
  137. package/esm/model/store/ui/uiSelectors.d.ts.map +1 -1
  138. package/esm/model/store/ui/uiSelectors.js +6 -0
  139. package/esm/model/store/ui/uiState.d.ts +5 -0
  140. package/esm/model/store/ui/uiState.d.ts.map +1 -1
  141. package/esm/model/store/ui/uiState.js +1 -0
  142. package/esm/model/types/commonTypes.d.ts +38 -22
  143. package/esm/model/types/commonTypes.d.ts.map +1 -1
  144. package/esm/model/types/insightEditorTypes.d.ts +43 -0
  145. package/esm/model/types/insightEditorTypes.d.ts.map +1 -0
  146. package/esm/model/types/insightEditorTypes.js +2 -0
  147. package/esm/model/utils/attributeFilterMatching.d.ts +10 -0
  148. package/esm/model/utils/attributeFilterMatching.d.ts.map +1 -1
  149. package/esm/model/utils/attributeFilterMatching.js +14 -3
  150. package/esm/model/utils/attributeFilterSelectionTypeCompatibility.d.ts +8 -0
  151. package/esm/model/utils/attributeFilterSelectionTypeCompatibility.d.ts.map +1 -1
  152. package/esm/model/utils/attributeFilterSelectionTypeCompatibility.js +14 -4
  153. package/esm/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/components/RecipientsSelect/LiveRegionLastSelectContainer.d.ts +9 -0
  154. package/esm/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/components/RecipientsSelect/LiveRegionLastSelectContainer.d.ts.map +1 -0
  155. package/esm/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/components/RecipientsSelect/LiveRegionLastSelectContainer.js +12 -0
  156. package/esm/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/components/RecipientsSelect/RecipientsSelect.d.ts.map +1 -1
  157. package/esm/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/components/RecipientsSelect/RecipientsSelect.js +3 -3
  158. package/esm/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/components/RecipientsSelect/RecipientsSelectRenderer.d.ts.map +1 -1
  159. package/esm/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/components/RecipientsSelect/RecipientsSelectRenderer.js +14 -7
  160. package/esm/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/components/RecipientsSelect/RecipientsSelectionDescription.d.ts +12 -0
  161. package/esm/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/components/RecipientsSelect/RecipientsSelectionDescription.d.ts.map +1 -0
  162. package/esm/presentation/automations/scheduledEmail/DefaultScheduledEmailDialog/components/RecipientsSelect/RecipientsSelectionDescription.js +30 -0
  163. package/esm/presentation/automations/scheduledEmail/utils/users.d.ts +1 -0
  164. package/esm/presentation/automations/scheduledEmail/utils/users.d.ts.map +1 -1
  165. package/esm/presentation/automations/scheduledEmail/utils/users.js +3 -0
  166. package/esm/presentation/automations/shared/automationFilters/utils.d.ts +4 -0
  167. package/esm/presentation/automations/shared/automationFilters/utils.d.ts.map +1 -1
  168. package/esm/presentation/automations/shared/automationFilters/utils.js +12 -5
  169. package/esm/presentation/dashboard/DashboardHeader/DashboardHeader.d.ts +1 -1
  170. package/esm/presentation/dashboard/DashboardHeader/DashboardHeader.d.ts.map +1 -1
  171. package/esm/presentation/dashboard/DashboardHeader/DashboardHeader.js +2 -11
  172. package/esm/presentation/dashboard/DashboardSidebar/CreationPanel.d.ts.map +1 -1
  173. package/esm/presentation/dashboard/DashboardSidebar/CreationPanel.js +10 -1
  174. package/esm/presentation/dashboard/DefaultDashboardContent/DashboardContentLoader.d.ts +5 -0
  175. package/esm/presentation/dashboard/DefaultDashboardContent/DashboardContentLoader.d.ts.map +1 -0
  176. package/esm/presentation/dashboard/DefaultDashboardContent/DashboardContentLoader.js +27 -0
  177. package/esm/presentation/dashboard/DefaultDashboardContent/DefaultDashboardMainContent.d.ts.map +1 -1
  178. package/esm/presentation/dashboard/DefaultDashboardContent/DefaultDashboardMainContent.js +7 -2
  179. package/esm/presentation/dashboard/components/DashboardAiComposingOverlay.d.ts +4 -0
  180. package/esm/presentation/dashboard/components/DashboardAiComposingOverlay.d.ts.map +1 -0
  181. package/esm/presentation/dashboard/components/DashboardAiComposingOverlay.js +151 -0
  182. package/esm/presentation/dashboard/components/DashboardInner.d.ts.map +1 -1
  183. package/esm/presentation/dashboard/components/DashboardInner.js +17 -14
  184. package/esm/presentation/dashboard/components/DashboardInteractionComposing.d.ts +8 -0
  185. package/esm/presentation/dashboard/components/DashboardInteractionComposing.d.ts.map +1 -0
  186. package/esm/presentation/dashboard/components/DashboardInteractionComposing.js +23 -0
  187. package/esm/presentation/dashboard/components/DashboardInteractionLock.d.ts +8 -0
  188. package/esm/presentation/dashboard/components/DashboardInteractionLock.d.ts.map +1 -0
  189. package/esm/presentation/dashboard/components/DashboardInteractionLock.js +29 -0
  190. package/esm/presentation/dashboard/hooks/useDashboard.d.ts.map +1 -1
  191. package/esm/presentation/dashboard/hooks/useDashboard.js +3 -2
  192. package/esm/presentation/dashboard/types.d.ts +5 -0
  193. package/esm/presentation/dashboard/types.d.ts.map +1 -1
  194. package/esm/presentation/dragAndDrop/draggableWidget/AddDashboardLayoutWidgetButton.d.ts.map +1 -1
  195. package/esm/presentation/dragAndDrop/draggableWidget/AddDashboardLayoutWidgetButton.js +2 -4
  196. package/esm/presentation/dragAndDrop/draggableWidget/AddRichTextWidgetButton.d.ts.map +1 -1
  197. package/esm/presentation/dragAndDrop/draggableWidget/AddRichTextWidgetButton.js +3 -3
  198. package/esm/presentation/dragAndDrop/draggableWidget/AddVisualizationSwitcherWidgetButton.d.ts.map +1 -1
  199. package/esm/presentation/dragAndDrop/draggableWidget/AddVisualizationSwitcherWidgetButton.js +2 -4
  200. package/esm/presentation/filterBar/attributeFilter/addAttributeFilter/AddAttributeFilterPlaceholder.d.ts.map +1 -1
  201. package/esm/presentation/filterBar/attributeFilter/addAttributeFilter/AddAttributeFilterPlaceholder.js +3 -2
  202. package/esm/presentation/filterBar/filterBar/hooks/useResetFiltersButton.d.ts.map +1 -1
  203. package/esm/presentation/filterBar/filterBar/hooks/useResetFiltersButton.js +4 -1
  204. package/esm/presentation/insightEditor/InsightEditorContent.d.ts +26 -0
  205. package/esm/presentation/insightEditor/InsightEditorContent.d.ts.map +1 -0
  206. package/esm/presentation/insightEditor/InsightEditorContent.js +179 -0
  207. package/esm/presentation/insightEditor/InsightEditorCreatePanelItem.d.ts +7 -0
  208. package/esm/presentation/insightEditor/InsightEditorCreatePanelItem.d.ts.map +1 -0
  209. package/esm/presentation/insightEditor/InsightEditorCreatePanelItem.js +25 -0
  210. package/esm/presentation/insightEditor/InsightEditorOverlay.d.ts +6 -0
  211. package/esm/presentation/insightEditor/InsightEditorOverlay.d.ts.map +1 -0
  212. package/esm/presentation/insightEditor/InsightEditorOverlay.js +110 -0
  213. package/esm/presentation/insightEditor/InsightEditorPermissionsContext.d.ts +10 -0
  214. package/esm/presentation/insightEditor/InsightEditorPermissionsContext.d.ts.map +1 -0
  215. package/esm/presentation/insightEditor/InsightEditorPermissionsContext.js +18 -0
  216. package/esm/presentation/insightEditor/InsightEditorPlaceholder.d.ts +7 -0
  217. package/esm/presentation/insightEditor/InsightEditorPlaceholder.d.ts.map +1 -0
  218. package/esm/presentation/insightEditor/InsightEditorPlaceholder.js +15 -0
  219. package/esm/presentation/insightEditor/filterContext/attributeFilters.d.ts +4 -0
  220. package/esm/presentation/insightEditor/filterContext/attributeFilters.d.ts.map +1 -0
  221. package/esm/presentation/insightEditor/filterContext/attributeFilters.js +115 -0
  222. package/esm/presentation/insightEditor/filterContext/dateFilters.d.ts +5 -0
  223. package/esm/presentation/insightEditor/filterContext/dateFilters.d.ts.map +1 -0
  224. package/esm/presentation/insightEditor/filterContext/dateFilters.js +117 -0
  225. package/esm/presentation/insightEditor/filterContext/filterIntersection.d.ts +7 -0
  226. package/esm/presentation/insightEditor/filterContext/filterIntersection.d.ts.map +1 -0
  227. package/esm/presentation/insightEditor/filterContext/filterIntersection.js +149 -0
  228. package/esm/presentation/insightEditor/filterContext/insightFilterContext.d.ts +26 -0
  229. package/esm/presentation/insightEditor/filterContext/insightFilterContext.d.ts.map +1 -0
  230. package/esm/presentation/insightEditor/filterContext/insightFilterContext.js +72 -0
  231. package/esm/presentation/insightEditor/filterContext/measureValueFilters.d.ts +20 -0
  232. package/esm/presentation/insightEditor/filterContext/measureValueFilters.d.ts.map +1 -0
  233. package/esm/presentation/insightEditor/filterContext/measureValueFilters.js +76 -0
  234. package/esm/presentation/insightEditor/filterContext/objQualifiers.d.ts +11 -0
  235. package/esm/presentation/insightEditor/filterContext/objQualifiers.d.ts.map +1 -0
  236. package/esm/presentation/insightEditor/filterContext/objQualifiers.js +18 -0
  237. package/esm/presentation/insightEditor/insightEditorEscapeGuard.d.ts +18 -0
  238. package/esm/presentation/insightEditor/insightEditorEscapeGuard.d.ts.map +1 -0
  239. package/esm/presentation/insightEditor/insightEditorEscapeGuard.js +24 -0
  240. package/esm/presentation/insightEditor/insightEditorRoute.d.ts +34 -0
  241. package/esm/presentation/insightEditor/insightEditorRoute.d.ts.map +1 -0
  242. package/esm/presentation/insightEditor/insightEditorRoute.js +48 -0
  243. package/esm/presentation/insightEditor/types.d.ts +7 -0
  244. package/esm/presentation/insightEditor/types.d.ts.map +1 -0
  245. package/esm/presentation/insightEditor/types.js +2 -0
  246. package/esm/presentation/insightEditor/useCanCreateInsightInEditor.d.ts +5 -0
  247. package/esm/presentation/insightEditor/useCanCreateInsightInEditor.d.ts.map +1 -0
  248. package/esm/presentation/insightEditor/useCanCreateInsightInEditor.js +14 -0
  249. package/esm/presentation/insightEditor/useExploreFilterTransfer.d.ts +21 -0
  250. package/esm/presentation/insightEditor/useExploreFilterTransfer.d.ts.map +1 -0
  251. package/esm/presentation/insightEditor/useExploreFilterTransfer.js +193 -0
  252. package/esm/presentation/insightEditor/useInsightEditPermissions.d.ts +17 -0
  253. package/esm/presentation/insightEditor/useInsightEditPermissions.d.ts.map +1 -0
  254. package/esm/presentation/insightEditor/useInsightEditPermissions.js +42 -0
  255. package/esm/presentation/insightEditor/useInsightEditorMenuItems.d.ts +23 -0
  256. package/esm/presentation/insightEditor/useInsightEditorMenuItems.d.ts.map +1 -0
  257. package/esm/presentation/insightEditor/useInsightEditorMenuItems.js +126 -0
  258. package/esm/presentation/insightEditor/useInsightEditorSession.d.ts +19 -0
  259. package/esm/presentation/insightEditor/useInsightEditorSession.d.ts.map +1 -0
  260. package/esm/presentation/insightEditor/useInsightEditorSession.js +141 -0
  261. package/esm/presentation/insightList/InsightList.d.ts.map +1 -1
  262. package/esm/presentation/insightList/InsightList.js +10 -5
  263. package/esm/presentation/localization/bundles/de-DE.localization-bundle.d.ts +6 -0
  264. package/esm/presentation/localization/bundles/de-DE.localization-bundle.d.ts.map +1 -1
  265. package/esm/presentation/localization/bundles/de-DE.localization-bundle.js +48 -42
  266. package/esm/presentation/localization/bundles/en-AU.localization-bundle.d.ts +6 -0
  267. package/esm/presentation/localization/bundles/en-AU.localization-bundle.d.ts.map +1 -1
  268. package/esm/presentation/localization/bundles/en-AU.localization-bundle.js +6 -0
  269. package/esm/presentation/localization/bundles/en-GB.localization-bundle.d.ts +6 -0
  270. package/esm/presentation/localization/bundles/en-GB.localization-bundle.d.ts.map +1 -1
  271. package/esm/presentation/localization/bundles/en-GB.localization-bundle.js +11 -5
  272. package/esm/presentation/localization/bundles/en-US.localization-bundle.d.ts +56 -0
  273. package/esm/presentation/localization/bundles/en-US.localization-bundle.d.ts.map +1 -1
  274. package/esm/presentation/localization/bundles/en-US.localization-bundle.js +56 -0
  275. package/esm/presentation/localization/bundles/es-419.localization-bundle.d.ts +6 -0
  276. package/esm/presentation/localization/bundles/es-419.localization-bundle.d.ts.map +1 -1
  277. package/esm/presentation/localization/bundles/es-419.localization-bundle.js +6 -0
  278. package/esm/presentation/localization/bundles/es-ES.localization-bundle.d.ts +6 -0
  279. package/esm/presentation/localization/bundles/es-ES.localization-bundle.d.ts.map +1 -1
  280. package/esm/presentation/localization/bundles/es-ES.localization-bundle.js +6 -0
  281. package/esm/presentation/localization/bundles/fi-FI.localization-bundle.d.ts +6 -0
  282. package/esm/presentation/localization/bundles/fi-FI.localization-bundle.d.ts.map +1 -1
  283. package/esm/presentation/localization/bundles/fi-FI.localization-bundle.js +6 -0
  284. package/esm/presentation/localization/bundles/fr-CA.localization-bundle.d.ts +6 -0
  285. package/esm/presentation/localization/bundles/fr-CA.localization-bundle.d.ts.map +1 -1
  286. package/esm/presentation/localization/bundles/fr-CA.localization-bundle.js +6 -0
  287. package/esm/presentation/localization/bundles/fr-FR.localization-bundle.d.ts +6 -0
  288. package/esm/presentation/localization/bundles/fr-FR.localization-bundle.d.ts.map +1 -1
  289. package/esm/presentation/localization/bundles/fr-FR.localization-bundle.js +187 -181
  290. package/esm/presentation/localization/bundles/id-ID.localization-bundle.d.ts +6 -0
  291. package/esm/presentation/localization/bundles/id-ID.localization-bundle.d.ts.map +1 -1
  292. package/esm/presentation/localization/bundles/id-ID.localization-bundle.js +6 -0
  293. package/esm/presentation/localization/bundles/it-IT.localization-bundle.d.ts +6 -0
  294. package/esm/presentation/localization/bundles/it-IT.localization-bundle.d.ts.map +1 -1
  295. package/esm/presentation/localization/bundles/it-IT.localization-bundle.js +6 -0
  296. package/esm/presentation/localization/bundles/ja-JP.localization-bundle.d.ts +6 -0
  297. package/esm/presentation/localization/bundles/ja-JP.localization-bundle.d.ts.map +1 -1
  298. package/esm/presentation/localization/bundles/ja-JP.localization-bundle.js +6 -0
  299. package/esm/presentation/localization/bundles/ko-KR.localization-bundle.d.ts +6 -0
  300. package/esm/presentation/localization/bundles/ko-KR.localization-bundle.d.ts.map +1 -1
  301. package/esm/presentation/localization/bundles/ko-KR.localization-bundle.js +6 -0
  302. package/esm/presentation/localization/bundles/nl-NL.localization-bundle.d.ts +6 -0
  303. package/esm/presentation/localization/bundles/nl-NL.localization-bundle.d.ts.map +1 -1
  304. package/esm/presentation/localization/bundles/nl-NL.localization-bundle.js +6 -0
  305. package/esm/presentation/localization/bundles/pl-PL.localization-bundle.d.ts +6 -0
  306. package/esm/presentation/localization/bundles/pl-PL.localization-bundle.d.ts.map +1 -1
  307. package/esm/presentation/localization/bundles/pl-PL.localization-bundle.js +6 -0
  308. package/esm/presentation/localization/bundles/pt-BR.localization-bundle.d.ts +6 -0
  309. package/esm/presentation/localization/bundles/pt-BR.localization-bundle.d.ts.map +1 -1
  310. package/esm/presentation/localization/bundles/pt-BR.localization-bundle.js +6 -0
  311. package/esm/presentation/localization/bundles/pt-PT.localization-bundle.d.ts +6 -0
  312. package/esm/presentation/localization/bundles/pt-PT.localization-bundle.d.ts.map +1 -1
  313. package/esm/presentation/localization/bundles/pt-PT.localization-bundle.js +6 -0
  314. package/esm/presentation/localization/bundles/ru-RU.localization-bundle.d.ts +6 -0
  315. package/esm/presentation/localization/bundles/ru-RU.localization-bundle.d.ts.map +1 -1
  316. package/esm/presentation/localization/bundles/ru-RU.localization-bundle.js +6 -0
  317. package/esm/presentation/localization/bundles/sl-SI.localization-bundle.d.ts +6 -0
  318. package/esm/presentation/localization/bundles/sl-SI.localization-bundle.d.ts.map +1 -1
  319. package/esm/presentation/localization/bundles/sl-SI.localization-bundle.js +6 -0
  320. package/esm/presentation/localization/bundles/th-TH.localization-bundle.d.ts +6 -0
  321. package/esm/presentation/localization/bundles/th-TH.localization-bundle.d.ts.map +1 -1
  322. package/esm/presentation/localization/bundles/th-TH.localization-bundle.js +6 -0
  323. package/esm/presentation/localization/bundles/tr-TR.localization-bundle.d.ts +6 -0
  324. package/esm/presentation/localization/bundles/tr-TR.localization-bundle.d.ts.map +1 -1
  325. package/esm/presentation/localization/bundles/tr-TR.localization-bundle.js +6 -0
  326. package/esm/presentation/localization/bundles/uk-UA.localization-bundle.d.ts +6 -0
  327. package/esm/presentation/localization/bundles/uk-UA.localization-bundle.d.ts.map +1 -1
  328. package/esm/presentation/localization/bundles/uk-UA.localization-bundle.js +6 -0
  329. package/esm/presentation/localization/bundles/vi-VN.localization-bundle.d.ts +6 -0
  330. package/esm/presentation/localization/bundles/vi-VN.localization-bundle.d.ts.map +1 -1
  331. package/esm/presentation/localization/bundles/vi-VN.localization-bundle.js +6 -0
  332. package/esm/presentation/localization/bundles/zh-HK.localization-bundle.d.ts +6 -0
  333. package/esm/presentation/localization/bundles/zh-HK.localization-bundle.d.ts.map +1 -1
  334. package/esm/presentation/localization/bundles/zh-HK.localization-bundle.js +6 -0
  335. package/esm/presentation/localization/bundles/zh-Hans.localization-bundle.d.ts +6 -0
  336. package/esm/presentation/localization/bundles/zh-Hans.localization-bundle.d.ts.map +1 -1
  337. package/esm/presentation/localization/bundles/zh-Hans.localization-bundle.js +6 -0
  338. package/esm/presentation/localization/bundles/zh-Hant.localization-bundle.d.ts +6 -0
  339. package/esm/presentation/localization/bundles/zh-Hant.localization-bundle.d.ts.map +1 -1
  340. package/esm/presentation/localization/bundles/zh-Hant.localization-bundle.js +7 -1
  341. package/esm/presentation/topBar/buttonBar/button/saveAsButton/DefaultSaveAsNewButton.d.ts.map +1 -1
  342. package/esm/presentation/topBar/buttonBar/button/saveAsButton/DefaultSaveAsNewButton.js +3 -2
  343. package/esm/presentation/topBar/buttonBar/button/saveButton/DefaultSaveButton.d.ts.map +1 -1
  344. package/esm/presentation/topBar/buttonBar/button/saveButton/DefaultSaveButton.js +3 -2
  345. package/esm/presentation/topBar/buttonBar/button/settingButton/DefaultSettingButton.d.ts.map +1 -1
  346. package/esm/presentation/topBar/buttonBar/button/settingButton/DefaultSettingButton.js +3 -2
  347. package/esm/presentation/topBar/buttonBar/button/shareButton/DefaultShareButton.d.ts.map +1 -1
  348. package/esm/presentation/topBar/buttonBar/button/shareButton/DefaultShareButton.js +3 -2
  349. package/esm/presentation/topBar/title/EditableTitle.d.ts.map +1 -1
  350. package/esm/presentation/topBar/title/EditableTitle.js +6 -0
  351. package/esm/presentation/widget/insight/DefaultDashboardInsightComponentSetFactory.d.ts +1 -1
  352. package/esm/presentation/widget/insight/DefaultDashboardInsightComponentSetFactory.d.ts.map +1 -1
  353. package/esm/presentation/widget/insight/DefaultDashboardInsightComponentSetFactory.js +13 -1
  354. package/esm/presentation/widget/insightMenu/DefaultDashboardInsightMenu/DashboardInsightMenu/index.d.ts.map +1 -1
  355. package/esm/presentation/widget/insightMenu/DefaultDashboardInsightMenu/DashboardInsightMenu/index.js +10 -1
  356. package/esm/presentation/widget/richText/EditModeDashboardRichText.js +1 -1
  357. package/esm/presentation/widget/visualizationSwitcher/EditModeDashboardVisualizationSwitcher.js +1 -1
  358. package/esm/presentation/widget/visualizationSwitcher/configuration/ToolbarTop.js +2 -2
  359. package/esm/presentation/widget/widget/InsightWidget/useEditableInsightMenu.d.ts.map +1 -1
  360. package/esm/presentation/widget/widget/InsightWidget/useEditableInsightMenu.js +5 -1
  361. package/esm/presentation/widget/widget/InsightWidget/useInsightMenu.d.ts.map +1 -1
  362. package/esm/presentation/widget/widget/InsightWidget/useInsightMenu.js +4 -1
  363. package/esm/sdk-ui-dashboard.d.ts +596 -69
  364. package/package.json +23 -21
  365. package/styles/css/alerting.css +1 -1
  366. package/styles/css/alerting.css.map +1 -1
  367. package/styles/css/attributeFilterConfig.css +3 -3
  368. package/styles/css/automationFilters.css +1 -1
  369. package/styles/css/configurationPanel.css +2 -2
  370. package/styles/css/configurationPanel.css.map +1 -1
  371. package/styles/css/dashboard.css +141 -87
  372. package/styles/css/dashboard.css.map +1 -1
  373. package/styles/css/dashboardContentLoader.css +45 -0
  374. package/styles/css/dashboardContentLoader.css.map +1 -0
  375. package/styles/css/dashboardNestedLayoutToolbar.css.map +1 -1
  376. package/styles/css/dashboardNestedLayoutWidget.css +1 -1
  377. package/styles/css/dashboardSettingsDialog.css +1 -1
  378. package/styles/css/dragAndDrop.css +0 -8
  379. package/styles/css/dragAndDrop.css.map +1 -1
  380. package/styles/css/drillConfigPanel.css +7 -7
  381. package/styles/css/drillConfigPanel.css.map +1 -1
  382. package/styles/css/filterBar.css +4 -4
  383. package/styles/css/filterBar.css.map +1 -1
  384. package/styles/css/grouped-navigation.css +2 -2
  385. package/styles/css/grouped-navigation.css.map +1 -1
  386. package/styles/css/icons.css.map +1 -1
  387. package/styles/css/insightConfiguration.css +1 -1
  388. package/styles/css/insightConfiguration.css.map +1 -1
  389. package/styles/css/insightEditor.css +46 -0
  390. package/styles/css/insightEditor.css.map +1 -0
  391. package/styles/css/kpi.css +2 -2
  392. package/styles/css/kpi.css.map +1 -1
  393. package/styles/css/kpi_alert_dialog.css +2 -2
  394. package/styles/css/layout.css +5 -5
  395. package/styles/css/layout.css.map +1 -1
  396. package/styles/css/main.css +336 -219
  397. package/styles/css/main.css.map +1 -1
  398. package/styles/css/notifications_channels_dialog.css +2 -2
  399. package/styles/css/notifications_channels_dialog.css.map +1 -1
  400. package/styles/css/resizing.css.map +1 -1
  401. package/styles/css/richTextWidget.css.map +1 -1
  402. package/styles/css/saveAsDialog.css.map +1 -1
  403. package/styles/css/sidebar.css +35 -10
  404. package/styles/css/sidebar.css.map +1 -1
  405. package/styles/css/topBar.css.map +1 -1
  406. package/styles/css/visualization.css.map +1 -1
  407. package/styles/css/visualizationSwitcherHeader.css.map +1 -1
  408. package/styles/css/visualizationSwitcherToolbar.css +1 -1
  409. package/styles/css/visualizationSwitcherToolbar.css.map +1 -1
  410. package/styles/css/visualizationSwitcherWidget.css +1 -1
  411. package/styles/css/visualizationSwitcherWidget.css.map +1 -1
  412. package/styles/scss/_variables.scss +3 -2
  413. package/styles/scss/_zIndexes.scss +4 -0
  414. package/styles/scss/attributeFilterConfig.scss +1 -1
  415. package/styles/scss/configurationPanel.scss +1 -1
  416. package/styles/scss/dashboard.scss +104 -96
  417. package/styles/scss/dashboardContentLoader.scss +45 -0
  418. package/styles/scss/dragAndDrop.scss +2 -9
  419. package/styles/scss/drillConfigPanel.scss +3 -3
  420. package/styles/scss/insightEditor.scss +53 -0
  421. package/styles/scss/layout.scss +1 -1
  422. package/styles/scss/main.scss +1 -0
  423. package/styles/scss/sidebar.scss +47 -12
  424. package/esm/presentation/dashboard/components/DashboardSkeleton.d.ts +0 -5
  425. package/esm/presentation/dashboard/components/DashboardSkeleton.d.ts.map +0 -1
  426. package/esm/presentation/dashboard/components/DashboardSkeleton.js +0 -20
@@ -1,11 +1,36 @@
1
1
  // (C) 2021-2026 GoodData Corporation
2
+ import { cloneDeep } from "lodash-es";
2
3
  import { invariant } from "ts-invariant";
3
- import { areObjRefsEqual, attributeElementsIsEmpty, dashboardAttributeFilterItemLocalIdentifier, dashboardFilterLocalIdentifier, isAllTimeDashboardDateFilter, isAttributeElementsByRef, isDashboardArbitraryAttributeFilter, isDashboardAttributeFilter, isDashboardAttributeFilterItem, isDashboardCommonDateFilter, isDashboardDateFilter, isDashboardMatchAttributeFilter, isDashboardMeasureValueFilter, newAllTimeDashboardDateFilter, } from "@gooddata/sdk-model";
4
+ import { areObjRefsEqual, attributeElementsIsEmpty, dashboardAttributeFilterItemDisplayForm, dashboardAttributeFilterItemLocalIdentifier, dashboardFilterLocalIdentifier, isAttributeElementsByRef, isDashboardArbitraryAttributeFilter, isDashboardAttributeFilter, isDashboardAttributeFilterItem, isDashboardCommonDateFilter, isDashboardDateFilter, isDashboardDateFilterWithDimension, isDashboardMatchAttributeFilter, isDashboardMeasureValueFilter, isIdentifierRef, newAllTimeDashboardDateFilter, } from "@gooddata/sdk-model";
5
+ import { isDisplayFormMetadataReferencedBy } from "../../../utils/attributeFilterMatching.js";
4
6
  import { generateFilterLocalIdentifier, generateMeasureValueFilterLocalIdentifier, } from "../../_infra/generators.js";
5
- import { getActiveTab, getTabOrActive, getTabsToMutate, } from "../tabsState.js";
6
- import { filterContextInitialState } from "./filterContextState.js";
7
+ import { getActiveTab, getTabOrActive } from "../tabsState.js";
8
+ import { filterContextInitialState, } from "./filterContextState.js";
7
9
  import { applyFilterContext, initializeFilterContext } from "./filterContextUtils.js";
8
- import { findCounterpartFilter, findOriginalFilter, isCrossFilteringVirtualFilter, preserveFilterDependencies, retargetAttributeFilterItem, } from "./filterSync.js";
10
+ import { findOriginalFilter, preserveFilterDependencies } from "./filterSync.js";
11
+ /**
12
+ * Filter context of the tab addressed by `tabLocalIdentifier`, or of the active tab when omitted.
13
+ * Initializes an empty filter context on a tab that has none.
14
+ */
15
+ function getTabFilterContext(state, tabLocalIdentifier) {
16
+ const tab = getTabOrActive(state, tabLocalIdentifier);
17
+ if (!tab) {
18
+ return undefined;
19
+ }
20
+ if (!tab.filterContext) {
21
+ tab.filterContext = { ...filterContextInitialState };
22
+ }
23
+ return { tab, filterContext: tab.filterContext };
24
+ }
25
+ function getEditableFilterContext(state, tabLocalIdentifier) {
26
+ const target = getTabFilterContext(state, tabLocalIdentifier);
27
+ if (!target) {
28
+ return undefined;
29
+ }
30
+ const definition = target.filterContext.filterContextDefinition;
31
+ invariant(definition, "Attempt to edit uninitialized filter context");
32
+ return { ...target, definition };
33
+ }
9
34
  const setFilterContext = (state, action) => {
10
35
  const activeTab = getActiveTab(state);
11
36
  if (!activeTab) {
@@ -46,42 +71,34 @@ const updateFilterContextIdentityForTab = (state, action) => {
46
71
  }
47
72
  tab.filterContext.filterContextIdentity = filterContextIdentity;
48
73
  };
49
- //
50
- //
51
- //
52
74
  const removeAttributeFilterDisplayForms = (state, action) => {
53
- const activeTab = getActiveTab(state);
54
- if (!activeTab) {
75
+ const { displayForm, tabLocalIdentifier } = action.payload;
76
+ const target = getTabFilterContext(state, tabLocalIdentifier);
77
+ if (!target) {
55
78
  return;
56
79
  }
57
- if (!activeTab.filterContext) {
58
- activeTab.filterContext = { ...filterContextInitialState };
59
- }
60
- invariant(activeTab.filterContext.attributeFilterDisplayForms, "attempting to work with uninitialized state");
61
- activeTab.filterContext.attributeFilterDisplayForms =
62
- activeTab.filterContext.attributeFilterDisplayForms.filter((df) => {
63
- return !areObjRefsEqual(df, action.payload);
64
- });
80
+ const { filterContext } = target;
81
+ invariant(filterContext.attributeFilterDisplayForms, "attempting to work with uninitialized state");
82
+ // A cross-filtering virtual filter shares its display form with the dashboard filter it duplicates.
83
+ const usedDisplayForms = (filterContext.filterContextDefinition?.filters ?? [])
84
+ .filter(isDashboardAttributeFilterItem)
85
+ .map(dashboardAttributeFilterItemDisplayForm);
86
+ filterContext.attributeFilterDisplayForms = filterContext.attributeFilterDisplayForms.filter((df) => !isDisplayFormMetadataReferencedBy(df, displayForm) ||
87
+ usedDisplayForms.some((used) => isDisplayFormMetadataReferencedBy(df, used)));
65
88
  };
66
89
  const addAttributeFilterDisplayForm = (state, action) => {
67
90
  // Handle both old and new payload formats for backward compatibility
68
91
  const displayForm = "displayForm" in action.payload ? action.payload.displayForm : action.payload;
69
92
  const tabLocalIdentifier = "tabLocalIdentifier" in action.payload ? action.payload.tabLocalIdentifier : undefined;
70
- const targetTab = getTabOrActive(state, tabLocalIdentifier);
71
- if (!targetTab) {
93
+ const target = getTabFilterContext(state, tabLocalIdentifier);
94
+ if (!target) {
72
95
  return;
73
96
  }
74
- if (!targetTab.filterContext) {
75
- targetTab.filterContext = { ...filterContextInitialState };
76
- }
77
- invariant(targetTab.filterContext.attributeFilterDisplayForms, "attempting to work with uninitialized state");
97
+ const { filterContext } = target;
98
+ invariant(filterContext.attributeFilterDisplayForms, "attempting to work with uninitialized state");
78
99
  // if there is already a display form with the same ref, replace it
79
- const existing = targetTab.filterContext.attributeFilterDisplayForms.find((df) => areObjRefsEqual(df, displayForm.ref));
80
- if (existing) {
81
- targetTab.filterContext.attributeFilterDisplayForms =
82
- targetTab.filterContext.attributeFilterDisplayForms.filter((df) => !areObjRefsEqual(df, displayForm));
83
- }
84
- targetTab.filterContext.attributeFilterDisplayForms.push(displayForm);
100
+ filterContext.attributeFilterDisplayForms = filterContext.attributeFilterDisplayForms.filter((df) => !isDisplayFormMetadataReferencedBy(df, displayForm.ref));
101
+ filterContext.attributeFilterDisplayForms.push(displayForm);
85
102
  };
86
103
  //
87
104
  //
@@ -92,88 +109,19 @@ const setPreloadedAttributesWithReferences = (state, action) => {
92
109
  state.attributesWithReferences = action.payload;
93
110
  };
94
111
  const upsertDateFilter = (state, action) => {
95
- const { tabLocalIdentifier, enablePersistentFiltersAcrossTabs, isWorkingSelectionChange } = action.payload;
96
- const tabs = getTabsToMutate(state, tabLocalIdentifier, enablePersistentFiltersAcrossTabs, isWorkingSelectionChange);
97
- const source = tabs[0];
98
- if (!source) {
112
+ const tab = getTabOrActive(state, action.payload.tabLocalIdentifier);
113
+ if (!tab) {
99
114
  return;
100
115
  }
101
- upsertDateFilterOnTab(source, action.payload, true);
102
- for (const tab of tabs.slice(1)) {
103
- const payload = action.payload.restoreTargetOriginal
104
- ? restoredDateFilterPayload(tab, action.payload)
105
- : action.payload;
106
- if (!payload) {
107
- continue;
108
- }
109
- upsertDateFilterOnTab(tab, payload, false);
110
- }
116
+ upsertDateFilterOnTab(tab, action.payload);
111
117
  };
112
- /**
113
- * Saved date filter for a non-source tab during reset.
114
- * A common date filter with no saved original is all time, which is stored as the filter's absence.
115
- * A dimension date filter with no counterpart, or no saved original, is left unchanged.
116
- */
117
- function restoredDateFilterPayload(tab, payload) {
118
- const applied = findAppliedDateFilter(tab, payload.dataSet);
119
- if (!applied && payload.dataSet) {
120
- return undefined;
121
- }
122
- const original = findOriginalFilter(tab, applied ?? {
123
- dateFilter: { type: "relative", granularity: "GDC.time.date" },
124
- });
125
- if (isDashboardDateFilter(original)) {
126
- return toUpsertDateFilterPayload(original, payload);
127
- }
128
- if (!payload.dataSet) {
129
- return { type: "allTime" };
130
- }
131
- return undefined;
132
- }
133
- function findAppliedDateFilter(tab, dataSet) {
134
- const filters = tab.filterContext?.filterContextDefinition?.filters;
135
- if (!filters) {
136
- return undefined;
137
- }
138
- if (dataSet) {
139
- return filters.find((item) => isDashboardDateFilter(item) && areObjRefsEqual(item.dateFilter.dataSet, dataSet));
140
- }
141
- return filters.find(isDashboardCommonDateFilter);
142
- }
143
- function toUpsertDateFilterPayload(filter, source) {
144
- const { dataSet, localIdentifier, emptyValueHandling, boundedFilter, type, granularity, from, to } = filter.dateFilter;
145
- const shared = {
146
- ...(dataSet ? { dataSet } : {}),
147
- ...(localIdentifier ? { localIdentifier } : {}),
148
- ...(emptyValueHandling ? { emptyValueHandling } : {}),
149
- ...(source.isWorkingSelectionChange ? { isWorkingSelectionChange: true } : {}),
150
- ...(source.tabLocalIdentifier ? { tabLocalIdentifier: source.tabLocalIdentifier } : {}),
151
- };
152
- if (isAllTimeDashboardDateFilter(filter)) {
153
- return { type: "allTime", ...shared };
154
- }
155
- return {
156
- type,
157
- granularity,
158
- from,
159
- to,
160
- ...(boundedFilter ? { boundedFilter } : {}),
161
- ...shared,
162
- };
163
- }
164
- function upsertDateFilterOnTab(targetTab, payload, isSource) {
118
+ function upsertDateFilterOnTab(targetTab, payload) {
165
119
  if (!targetTab.filterContext) {
166
- if (!isSource) {
167
- return;
168
- }
169
120
  targetTab.filterContext = { ...filterContextInitialState };
170
121
  }
171
122
  const filterContextDefinition = payload.isWorkingSelectionChange
172
123
  ? targetTab.filterContext.workingFilterContextDefinition
173
124
  : targetTab.filterContext.filterContextDefinition;
174
- if (!isSource && !filterContextDefinition?.filters) {
175
- return;
176
- }
177
125
  invariant(filterContextDefinition?.filters, "Attempt to edit uninitialized filter context");
178
126
  const dateDataSet = payload.dataSet;
179
127
  let existingFilterIndex = -1;
@@ -183,9 +131,6 @@ function upsertDateFilterOnTab(targetTab, payload, isSource) {
183
131
  else {
184
132
  existingFilterIndex = filterContextDefinition.filters.findIndex((item) => isDashboardCommonDateFilter(item));
185
133
  }
186
- if (!isSource && existingFilterIndex < 0 && dateDataSet) {
187
- return;
188
- }
189
134
  if (payload.isWorkingSelectionChange && payload.type === "allTime") {
190
135
  // we need to allways set working filter to distinguish between "not changed" and "all time" filter
191
136
  if (existingFilterIndex >= 0) {
@@ -207,15 +152,9 @@ function upsertDateFilterOnTab(targetTab, payload, isSource) {
207
152
  }
208
153
  }
209
154
  else {
210
- const existing = filterContextDefinition.filters[existingFilterIndex];
211
- const preservedLocalIdentifier = isSource
212
- ? payload.localIdentifier
213
- : isDashboardDateFilter(existing)
214
- ? existing.dateFilter.localIdentifier
215
- : undefined;
216
155
  if (payload.emptyValueHandling) {
217
156
  // Preserve all-time common DF when it carries additional config (e.g. emptyValueHandling).
218
- filterContextDefinition.filters[existingFilterIndex] = newAllTimeDashboardDateFilter(undefined, preservedLocalIdentifier, payload.emptyValueHandling);
157
+ filterContextDefinition.filters[existingFilterIndex] = newAllTimeDashboardDateFilter(undefined, payload.localIdentifier, payload.emptyValueHandling);
219
158
  }
220
159
  else {
221
160
  // if allTime common DF remove the date filter altogether
@@ -236,7 +175,7 @@ function upsertDateFilterOnTab(targetTab, payload, isSource) {
236
175
  dateFilter.dateFilter.granularity = granularity;
237
176
  dateFilter.dateFilter.from = from;
238
177
  dateFilter.dateFilter.to = to;
239
- if (localIdentifier && isSource) {
178
+ if (localIdentifier) {
240
179
  dateFilter.dateFilter.localIdentifier = localIdentifier;
241
180
  }
242
181
  if (boundedFilter) {
@@ -253,7 +192,7 @@ function upsertDateFilterOnTab(targetTab, payload, isSource) {
253
192
  }
254
193
  }
255
194
  }
256
- else if (isSource || !dateDataSet) {
195
+ else {
257
196
  const { type, granularity, from, to, dataSet, localIdentifier, boundedFilter, emptyValueHandling } = payload;
258
197
  filterContextDefinition.filters.unshift({
259
198
  dateFilter: {
@@ -270,69 +209,22 @@ function upsertDateFilterOnTab(targetTab, payload, isSource) {
270
209
  }
271
210
  }
272
211
  const updateAttributeFilterSelection = (state, action) => {
273
- const { tabLocalIdentifier, enablePersistentFiltersAcrossTabs, isWorkingSelectionChange, crossFilteringLocalIdsByTab, } = action.payload;
274
- const tabs = getTabsToMutate(state, tabLocalIdentifier, enablePersistentFiltersAcrossTabs, isWorkingSelectionChange);
275
- const source = tabs[0];
276
- if (!source) {
277
- return;
278
- }
279
- const sourceFilter = findAppliedFilterByLocalId(source, action.payload.filterLocalId);
280
- updateAttributeFilterSelectionOnTab(source, action.payload, action.payload.filterLocalId, true);
281
- if (tabs.length === 1 ||
282
- !sourceFilter ||
283
- !isDashboardAttributeFilter(sourceFilter) ||
284
- isCrossFilteringVirtualFilter(source, action.payload.filterLocalId, crossFilteringLocalIdsByTab)) {
212
+ const tab = getTabOrActive(state, action.payload.tabLocalIdentifier);
213
+ if (!tab) {
285
214
  return;
286
215
  }
287
- for (const tab of tabs.slice(1)) {
288
- const match = findCounterpartFilter(source, sourceFilter, tab, state.attributesWithReferences, {
289
- allowListToText: false,
290
- crossFilteringLocalIdsByTab,
291
- });
292
- if (!match || !isDashboardAttributeFilter(match)) {
293
- continue;
294
- }
295
- const targetLocalId = match.attributeFilter.localIdentifier;
296
- if (!targetLocalId) {
297
- continue;
298
- }
299
- const original = action.payload.restoreTargetOriginal ? findOriginalFilter(tab, match) : undefined;
300
- if (action.payload.restoreTargetOriginal && !isDashboardAttributeFilter(original)) {
301
- continue;
302
- }
303
- updateAttributeFilterSelectionOnTab(tab, {
304
- ...action.payload,
305
- filterLocalId: targetLocalId,
306
- ...(isDashboardAttributeFilter(original)
307
- ? {
308
- elements: original.attributeFilter.attributeElements,
309
- negativeSelection: original.attributeFilter.negativeSelection,
310
- }
311
- : {}),
312
- enableImmediateAttributeFilterDisplayAsLabelMigration: false,
313
- isResultOfMigration: false,
314
- }, targetLocalId, false);
315
- }
216
+ updateAttributeFilterSelectionOnTab(tab, action.payload);
316
217
  };
317
- function updateAttributeFilterSelectionOnTab(targetTab, payload, filterLocalId, isSource) {
218
+ function updateAttributeFilterSelectionOnTab(targetTab, payload) {
318
219
  if (!targetTab.filterContext) {
319
- if (!isSource) {
320
- return;
321
- }
322
220
  targetTab.filterContext = { ...filterContextInitialState };
323
221
  }
324
- const { elements, negativeSelection, isWorkingSelectionChange, enableImmediateAttributeFilterDisplayAsLabelMigration, isResultOfMigration, isSelectionInvalid, } = payload;
222
+ const { filterLocalId, elements, negativeSelection, isWorkingSelectionChange, enableImmediateAttributeFilterDisplayAsLabelMigration, isResultOfMigration, isSelectionInvalid, } = payload;
325
223
  const filterContextDefinition = isWorkingSelectionChange
326
224
  ? targetTab.filterContext.workingFilterContextDefinition
327
225
  : targetTab.filterContext.filterContextDefinition;
328
- if (!isSource && !filterContextDefinition?.filters) {
329
- return;
330
- }
331
226
  invariant(filterContextDefinition?.filters, "Attempt to edit uninitialized filter context");
332
227
  const existingFilterIndex = filterContextDefinition.filters.findIndex((item) => isDashboardAttributeFilter(item) && item.attributeFilter.localIdentifier === filterLocalId);
333
- if (!isSource && existingFilterIndex < 0) {
334
- return;
335
- }
336
228
  invariant(existingFilterIndex >= 0 || isWorkingSelectionChange, "Attempt to update non-existing filter");
337
229
  if (isWorkingSelectionChange && existingFilterIndex >= 0) {
338
230
  filterContextDefinition.filters[existingFilterIndex] = {
@@ -393,60 +285,23 @@ function findAppliedFilterByLocalId(tab, localIdentifier) {
393
285
  return tab.filterContext?.filterContextDefinition?.filters.find((item) => dashboardFilterLocalIdentifier(item) === localIdentifier);
394
286
  }
395
287
  const replaceAttributeFilterItem = (state, action) => {
396
- const { tabLocalIdentifier, enablePersistentFiltersAcrossTabs, isWorkingSelectionChange, crossFilteringLocalIdsByTab, } = action.payload;
397
- const tabs = getTabsToMutate(state, tabLocalIdentifier, enablePersistentFiltersAcrossTabs, isWorkingSelectionChange);
398
- const source = tabs[0];
399
- if (!source) {
400
- return;
401
- }
402
- const sourceFilter = findAppliedFilterByLocalId(source, action.payload.filterLocalId);
403
- replaceAttributeFilterItemOnTab(source, action.payload, action.payload.filter, true);
404
- if (tabs.length === 1 ||
405
- !sourceFilter ||
406
- !isDashboardAttributeFilterItem(sourceFilter) ||
407
- isCrossFilteringVirtualFilter(source, action.payload.filterLocalId, crossFilteringLocalIdsByTab)) {
288
+ const tab = getTabOrActive(state, action.payload.tabLocalIdentifier);
289
+ if (!tab) {
408
290
  return;
409
291
  }
410
- for (const tab of tabs.slice(1)) {
411
- const match = findCounterpartFilter(source, sourceFilter, tab, state.attributesWithReferences, {
412
- allowListToText: true,
413
- crossFilteringLocalIdsByTab,
414
- });
415
- if (!match || !isDashboardAttributeFilterItem(match)) {
416
- continue;
417
- }
418
- const targetLocalId = dashboardAttributeFilterItemLocalIdentifier(match);
419
- if (!targetLocalId) {
420
- continue;
421
- }
422
- const original = action.payload.restoreTargetOriginal ? findOriginalFilter(tab, match) : undefined;
423
- if (action.payload.restoreTargetOriginal && !isDashboardAttributeFilterItem(original)) {
424
- continue;
425
- }
426
- const replacement = isDashboardAttributeFilterItem(original) ? original : action.payload.filter;
427
- replaceAttributeFilterItemOnTab(tab, { ...action.payload, filterLocalId: targetLocalId }, retargetAttributeFilterItem(replacement, targetLocalId), false);
428
- }
292
+ replaceAttributeFilterItemOnTab(tab, action.payload);
429
293
  };
430
- function replaceAttributeFilterItemOnTab(targetTab, payload, filter, isSource) {
294
+ function replaceAttributeFilterItemOnTab(targetTab, payload) {
431
295
  if (!targetTab.filterContext) {
432
- if (!isSource) {
433
- return;
434
- }
435
296
  targetTab.filterContext = { ...filterContextInitialState };
436
297
  }
437
- const { filterLocalId, isWorkingSelectionChange, isSelectionInvalid } = payload;
298
+ const { filterLocalId, filter, isWorkingSelectionChange, isSelectionInvalid } = payload;
438
299
  const filterContextDefinition = isWorkingSelectionChange
439
300
  ? targetTab.filterContext.workingFilterContextDefinition
440
301
  : targetTab.filterContext.filterContextDefinition;
441
- if (!isSource && !filterContextDefinition?.filters) {
442
- return;
443
- }
444
302
  invariant(filterContextDefinition?.filters, "Attempt to edit uninitialized filter context");
445
303
  const existingFilterIndex = filterContextDefinition.filters.findIndex((item) => isDashboardAttributeFilterItem(item) &&
446
304
  dashboardAttributeFilterItemLocalIdentifier(item) === filterLocalId);
447
- if (!isSource && existingFilterIndex < 0) {
448
- return;
449
- }
450
305
  // Preserve parent filter relationships from the existing filter when replacing.
451
306
  // The replacement filter typically only carries selection data, not dependency configuration.
452
307
  const replacementFilter = existingFilterIndex >= 0
@@ -480,17 +335,14 @@ function replaceAttributeFilterItemOnTab(targetTab, payload, filter, isSource) {
480
335
  }
481
336
  }
482
337
  const addAttributeFilter = (state, action) => {
483
- const activeTab = getActiveTab(state);
484
- if (!activeTab) {
338
+ const target = getEditableFilterContext(state, action.payload.tabLocalIdentifier);
339
+ if (!target) {
485
340
  return;
486
341
  }
487
- if (!activeTab.filterContext) {
488
- activeTab.filterContext = { ...filterContextInitialState };
489
- }
490
- invariant(activeTab.filterContext.filterContextDefinition, "Attempt to edit uninitialized filter context");
491
342
  const { displayForm, index, initialIsNegativeSelection, initialSelection, parentFilters, selectionMode, localIdentifier, title, } = action.payload;
343
+ const { definition } = target;
492
344
  // Filters are indexed just for attribute filters, if DateFilter is present should be always first item
493
- const isDateFilterPresent = activeTab.filterContext.filterContextDefinition.filters.findIndex(isDashboardDateFilter) >= 0;
345
+ const isDateFilterPresent = definition.filters.findIndex(isDashboardDateFilter) >= 0;
494
346
  const hasSelection = initialSelection && !attributeElementsIsEmpty(initialSelection);
495
347
  const isNegative = selectionMode !== "single" && (initialIsNegativeSelection || !hasSelection);
496
348
  // If DateFilter is present we have to move index by 1 because index of filter is calculated just for AttributeFilers array
@@ -500,117 +352,152 @@ const addAttributeFilter = (state, action) => {
500
352
  attributeElements: initialSelection ?? { uris: [] },
501
353
  displayForm,
502
354
  negativeSelection: isNegative,
503
- localIdentifier: localIdentifier ??
504
- generateFilterLocalIdentifier(displayForm, Math.max(0, attributeFilterIndex)),
355
+ localIdentifier: resolveAttributeFilterLocalIdentifier(definition, displayForm, attributeFilterIndex, localIdentifier),
505
356
  filterElementsBy: parentFilters ? [...parentFilters] : undefined,
506
357
  ...(selectionMode === undefined ? {} : { selectionMode }),
507
358
  title,
508
359
  },
509
360
  };
510
361
  if (index === -1) {
511
- activeTab.filterContext.filterContextDefinition.filters.push(filter);
362
+ definition.filters.push(filter);
512
363
  }
513
364
  else {
514
- activeTab.filterContext.filterContextDefinition.filters.splice(attributeFilterIndex, 0, filter);
365
+ definition.filters.splice(attributeFilterIndex, 0, filter);
515
366
  }
516
367
  };
517
368
  const addTextAttributeFilter = (state, action) => {
518
- const activeTab = getActiveTab(state);
519
- if (!activeTab) {
369
+ const target = getEditableFilterContext(state, action.payload.tabLocalIdentifier);
370
+ if (!target) {
520
371
  return;
521
372
  }
522
- if (!activeTab.filterContext) {
523
- activeTab.filterContext = { ...filterContextInitialState };
524
- }
525
- invariant(activeTab.filterContext.filterContextDefinition, "Attempt to edit uninitialized filter context");
526
373
  const { filter, index } = action.payload;
374
+ const { definition } = target;
527
375
  // Filters are indexed just for attribute filters, if DateFilter is present should be always first item
528
- const isDateFilterPresent = activeTab.filterContext.filterContextDefinition.filters.findIndex(isDashboardDateFilter) >= 0;
376
+ const isDateFilterPresent = definition.filters.findIndex(isDashboardDateFilter) >= 0;
529
377
  const attributeFilterIndex = isDateFilterPresent ? index + 1 : index;
530
- const generatedLocalIdentifierIndex = Math.max(0, attributeFilterIndex);
531
- const filterWithLocalIdentifier = isDashboardArbitraryAttributeFilter(filter)
532
- ? {
378
+ const filterWithLocalIdentifier = withAttributeFilterLocalIdentifier(filter, resolveAttributeFilterLocalIdentifier(definition, dashboardAttributeFilterItemDisplayForm(filter), attributeFilterIndex, dashboardAttributeFilterItemLocalIdentifier(filter)));
379
+ if (!filterWithLocalIdentifier) {
380
+ return;
381
+ }
382
+ if (index === -1) {
383
+ definition.filters.push(filterWithLocalIdentifier);
384
+ }
385
+ else {
386
+ definition.filters.splice(attributeFilterIndex, 0, filterWithLocalIdentifier);
387
+ }
388
+ };
389
+ function withAttributeFilterLocalIdentifier(filter, localIdentifier) {
390
+ if (isDashboardArbitraryAttributeFilter(filter)) {
391
+ return {
533
392
  arbitraryAttributeFilter: {
534
393
  ...filter.arbitraryAttributeFilter,
535
- localIdentifier: filter.arbitraryAttributeFilter.localIdentifier ??
536
- generateFilterLocalIdentifier(filter.arbitraryAttributeFilter.displayForm, generatedLocalIdentifierIndex),
394
+ localIdentifier,
395
+ },
396
+ };
397
+ }
398
+ if (isDashboardMatchAttributeFilter(filter)) {
399
+ return {
400
+ matchAttributeFilter: {
401
+ ...filter.matchAttributeFilter,
402
+ localIdentifier,
537
403
  },
404
+ };
405
+ }
406
+ return undefined;
407
+ }
408
+ /**
409
+ * Keeps `preferred` unless another filter on the tab already uses it, otherwise generates
410
+ * the first free identifier. A filter mirrored from another tab can collide with a local one.
411
+ */
412
+ function resolveAttributeFilterLocalIdentifier(definition, displayForm, index, preferred) {
413
+ return resolveFilterLocalIdentifier(definition, (candidateIndex) => generateFilterLocalIdentifier(displayForm, candidateIndex), index, preferred);
414
+ }
415
+ function resolveFilterLocalIdentifier(definition, generate, index, preferred) {
416
+ const used = new Set();
417
+ for (const item of definition.filters) {
418
+ const localIdentifier = dashboardFilterLocalIdentifier(item);
419
+ if (localIdentifier) {
420
+ used.add(localIdentifier);
538
421
  }
539
- : isDashboardMatchAttributeFilter(filter)
540
- ? {
541
- matchAttributeFilter: {
542
- ...filter.matchAttributeFilter,
543
- localIdentifier: filter.matchAttributeFilter.localIdentifier ??
544
- generateFilterLocalIdentifier(filter.matchAttributeFilter.displayForm, generatedLocalIdentifierIndex),
545
- },
546
- }
547
- : filter;
548
- if (index === -1) {
549
- activeTab.filterContext.filterContextDefinition.filters.push(filterWithLocalIdentifier);
550
422
  }
551
- else {
552
- activeTab.filterContext.filterContextDefinition.filters.splice(attributeFilterIndex, 0, filterWithLocalIdentifier);
423
+ if (preferred && !used.has(preferred)) {
424
+ return preferred;
553
425
  }
554
- };
426
+ let candidateIndex = Math.max(0, index);
427
+ let localIdentifier = generate(candidateIndex);
428
+ while (used.has(localIdentifier)) {
429
+ candidateIndex += 1;
430
+ localIdentifier = generate(candidateIndex);
431
+ }
432
+ return localIdentifier;
433
+ }
555
434
  const removeAttributeFilter = (state, action) => {
556
- const activeTab = getActiveTab(state);
557
- if (!activeTab) {
435
+ const target = getEditableFilterContext(state, action.payload.tabLocalIdentifier);
436
+ if (!target) {
558
437
  return;
559
438
  }
560
- if (!activeTab.filterContext) {
561
- activeTab.filterContext = { ...filterContextInitialState };
562
- }
563
- invariant(activeTab.filterContext.filterContextDefinition, "Attempt to edit uninitialized filter context");
564
439
  const { filterLocalId } = action.payload;
565
- const newFilters = activeTab.filterContext.filterContextDefinition.filters.filter((item) => isDashboardDateFilter(item) ||
566
- !isDashboardAttributeFilterItem(item) ||
567
- dashboardAttributeFilterItemLocalIdentifier(item) !== filterLocalId);
568
- activeTab.filterContext.filterContextDefinition = {
569
- ...activeTab.filterContext.filterContextDefinition,
570
- filters: newFilters,
440
+ const { filterContext, definition } = target;
441
+ filterContext.filterContextDefinition = {
442
+ ...definition,
443
+ filters: definition.filters.filter((item) => isDashboardDateFilter(item) ||
444
+ !isDashboardAttributeFilterItem(item) ||
445
+ dashboardAttributeFilterItemLocalIdentifier(item) !== filterLocalId),
571
446
  };
447
+ removeAttributeFilterParentReferences(filterContext.filterContextDefinition, filterLocalId);
572
448
  };
449
+ /**
450
+ * Drops parent references to a removed filter, including children that exist only on this tab.
451
+ */
452
+ function removeAttributeFilterParentReferences(definition, removedLocalId) {
453
+ for (const item of definition.filters) {
454
+ if (isDashboardAttributeFilter(item) && item.attributeFilter.filterElementsBy) {
455
+ const remaining = item.attributeFilter.filterElementsBy.filter((parent) => parent.filterLocalIdentifier !== removedLocalId);
456
+ if (remaining.length !== item.attributeFilter.filterElementsBy.length) {
457
+ item.attributeFilter.filterElementsBy = remaining;
458
+ }
459
+ }
460
+ else if (isDashboardArbitraryAttributeFilter(item) &&
461
+ item.arbitraryAttributeFilter.filterElementsBy) {
462
+ const remaining = item.arbitraryAttributeFilter.filterElementsBy.filter((parent) => parent.filterLocalIdentifier !== removedLocalId);
463
+ if (remaining.length !== item.arbitraryAttributeFilter.filterElementsBy.length) {
464
+ item.arbitraryAttributeFilter.filterElementsBy = remaining;
465
+ }
466
+ }
467
+ }
468
+ }
573
469
  const moveAttributeFilter = (state, action) => {
574
- const activeTab = getActiveTab(state);
575
- if (!activeTab) {
470
+ const target = getEditableFilterContext(state, action.payload.tabLocalIdentifier);
471
+ if (!target) {
576
472
  return;
577
473
  }
578
- if (!activeTab.filterContext) {
579
- activeTab.filterContext = { ...filterContextInitialState };
580
- }
581
- invariant(activeTab.filterContext.filterContextDefinition, "Attempt to edit uninitialized filter context");
582
474
  const { filterLocalId, index } = action.payload;
583
- const currentFilterIndex = activeTab.filterContext.filterContextDefinition.filters.findIndex((item) => isDashboardAttributeFilterItem(item) &&
475
+ const filters = target.definition.filters;
476
+ const currentFilterIndex = filters.findIndex((item) => isDashboardAttributeFilterItem(item) &&
584
477
  dashboardAttributeFilterItemLocalIdentifier(item) === filterLocalId);
585
478
  invariant(currentFilterIndex >= 0, "Attempt to move non-existing filter");
586
- const filter = activeTab.filterContext.filterContextDefinition.filters[currentFilterIndex];
587
- activeTab.filterContext.filterContextDefinition.filters.splice(currentFilterIndex, 1);
479
+ const filter = filters[currentFilterIndex];
480
+ filters.splice(currentFilterIndex, 1);
588
481
  // Filters are indexed just for attribute filters, if DateFilter is present should be always first item
589
- const isDateFilterPresent = activeTab.filterContext.filterContextDefinition.filters.findIndex(isDashboardDateFilter) >= 0;
482
+ const isDateFilterPresent = filters.findIndex(isDashboardDateFilter) >= 0;
590
483
  if (index === -1) {
591
- activeTab.filterContext.filterContextDefinition.filters.push(filter);
484
+ filters.push(filter);
592
485
  }
593
486
  else {
594
487
  // If DateFilter is present we have to move index by 1 because index of filter is calculated just for AttributeFilers array
595
488
  const attributeFilterIndex = isDateFilterPresent ? index + 1 : index;
596
- activeTab.filterContext.filterContextDefinition.filters.splice(attributeFilterIndex, 0, filter);
489
+ filters.splice(attributeFilterIndex, 0, filter);
597
490
  }
598
491
  };
599
492
  const setAttributeFilterParents = (state, action) => {
600
- const activeTab = getActiveTab(state);
601
- if (!activeTab) {
493
+ const target = getEditableFilterContext(state, action.payload.tabLocalIdentifier);
494
+ if (!target) {
602
495
  return;
603
496
  }
604
- if (!activeTab.filterContext) {
605
- activeTab.filterContext = { ...filterContextInitialState };
606
- }
607
- invariant(activeTab.filterContext.filterContextDefinition, "Attempt to edit uninitialized filter context");
608
497
  const { filterLocalId, parentFilters } = action.payload;
609
- const currentFilterIndex = activeTab.filterContext.filterContextDefinition.filters.findIndex((item) => isDashboardAttributeFilterItem(item) &&
610
- dashboardAttributeFilterItemLocalIdentifier(item) === filterLocalId);
611
- invariant(currentFilterIndex >= 0, "Attempt to set parent of a non-existing filter");
498
+ const filter = findAttributeFilterItem(target.definition, filterLocalId);
499
+ invariant(filter, "Attempt to set parent of a non-existing filter");
612
500
  // silently ignore when targeting match filter, will have no effect
613
- const filter = activeTab.filterContext.filterContextDefinition.filters[currentFilterIndex];
614
501
  if (isDashboardAttributeFilter(filter)) {
615
502
  filter.attributeFilter.filterElementsBy = [...parentFilters];
616
503
  }
@@ -619,19 +506,13 @@ const setAttributeFilterParents = (state, action) => {
619
506
  }
620
507
  };
621
508
  const setAttributeFilterDependentDateFilters = (state, action) => {
622
- const activeTab = getActiveTab(state);
623
- if (!activeTab) {
509
+ const target = getEditableFilterContext(state, action.payload.tabLocalIdentifier);
510
+ if (!target) {
624
511
  return;
625
512
  }
626
- if (!activeTab.filterContext) {
627
- activeTab.filterContext = { ...filterContextInitialState };
628
- }
629
- invariant(activeTab.filterContext.filterContextDefinition, "Attempt to edit uninitialized filter context");
630
513
  const { filterLocalId, dependentDateFilters } = action.payload;
631
- const currentFilterIndex = activeTab.filterContext.filterContextDefinition.filters.findIndex((item) => isDashboardAttributeFilterItem(item) &&
632
- dashboardAttributeFilterItemLocalIdentifier(item) === filterLocalId);
633
- invariant(currentFilterIndex >= 0, "Attempt to set dependent date filter of a non-existing filter");
634
- const filter = activeTab.filterContext.filterContextDefinition.filters[currentFilterIndex];
514
+ const filter = findAttributeFilterItem(target.definition, filterLocalId);
515
+ invariant(filter, "Attempt to set dependent date filter of a non-existing filter");
635
516
  if (isDashboardAttributeFilter(filter)) {
636
517
  filter.attributeFilter.filterElementsByDate = [...dependentDateFilters];
637
518
  }
@@ -640,64 +521,43 @@ const setAttributeFilterDependentDateFilters = (state, action) => {
640
521
  }
641
522
  // Match filters do not support filterElementsByDate
642
523
  };
524
+ function findAttributeFilterItem(definition, localIdentifier) {
525
+ return definition.filters.find((item) => isDashboardAttributeFilterItem(item) &&
526
+ dashboardAttributeFilterItemLocalIdentifier(item) === localIdentifier);
527
+ }
643
528
  const clearAttributeFiltersSelection = (state, action) => {
644
- const { tabLocalIdentifier, enablePersistentFiltersAcrossTabs, filterLocalIds, crossFilteringLocalIdsByTab, } = action.payload;
645
- const tabs = getTabsToMutate(state, tabLocalIdentifier, enablePersistentFiltersAcrossTabs);
646
- const source = tabs[0];
647
- if (!source) {
529
+ const { tabLocalIdentifier, filterLocalIds, restoreOriginal } = action.payload;
530
+ const tab = getTabOrActive(state, tabLocalIdentifier);
531
+ if (!tab) {
648
532
  return;
649
533
  }
650
- if (!source.filterContext) {
651
- source.filterContext = { ...filterContextInitialState };
534
+ if (!tab.filterContext) {
535
+ tab.filterContext = { ...filterContextInitialState };
652
536
  }
653
- const sourceFilters = filterLocalIds.map((filterLocalId) => ({
654
- filterLocalId,
655
- filter: findAppliedFilterByLocalId(source, filterLocalId),
656
- }));
657
- filterLocalIds.forEach((filterLocalId) => {
658
- clearAttributeFilterSelectionOnTab(source, filterLocalId, true);
659
- });
660
- for (const tab of tabs.slice(1)) {
661
- for (const { filterLocalId, filter } of sourceFilters) {
662
- if (!filter ||
663
- !isDashboardAttributeFilter(filter) ||
664
- isCrossFilteringVirtualFilter(source, filterLocalId, crossFilteringLocalIdsByTab)) {
665
- continue;
666
- }
667
- const match = findCounterpartFilter(source, filter, tab, state.attributesWithReferences, {
668
- allowListToText: false,
669
- crossFilteringLocalIdsByTab,
670
- });
671
- if (!match || !isDashboardAttributeFilter(match) || !match.attributeFilter.localIdentifier) {
672
- continue;
673
- }
674
- const targetLocalId = match.attributeFilter.localIdentifier;
675
- if (action.payload.restoreTargetOriginal) {
676
- const original = findOriginalFilter(tab, match);
677
- if (!isDashboardAttributeFilter(original)) {
678
- continue;
679
- }
680
- updateAttributeFilterSelectionOnTab(tab, {
681
- filterLocalId: targetLocalId,
682
- elements: original.attributeFilter.attributeElements,
683
- negativeSelection: original.attributeFilter.negativeSelection,
684
- }, targetLocalId, false);
685
- continue;
686
- }
687
- clearAttributeFilterSelectionOnTab(tab, targetLocalId, false);
537
+ for (const filterLocalId of filterLocalIds) {
538
+ if (restoreOriginal) {
539
+ restoreAttributeFilterSelectionOnTab(tab, filterLocalId);
540
+ }
541
+ else {
542
+ clearAttributeFilterSelectionOnTab(tab, filterLocalId);
688
543
  }
689
544
  }
690
545
  };
691
- function clearAttributeFilterSelectionOnTab(tab, filterLocalId, isSource) {
692
- const filters = tab.filterContext?.filterContextDefinition?.filters;
693
- if (!isSource && !filters) {
546
+ function restoreAttributeFilterSelectionOnTab(tab, filterLocalId) {
547
+ const filter = findAppliedFilterByLocalId(tab, filterLocalId);
548
+ const original = filter ? findOriginalFilter(tab, filter) : undefined;
549
+ if (!isDashboardAttributeFilter(original)) {
694
550
  return;
695
551
  }
552
+ updateAttributeFilterSelectionOnTab(tab, {
553
+ filterLocalId,
554
+ elements: original.attributeFilter.attributeElements,
555
+ negativeSelection: original.attributeFilter.negativeSelection,
556
+ });
557
+ }
558
+ function clearAttributeFilterSelectionOnTab(tab, filterLocalId) {
696
559
  invariant(tab.filterContext?.filterContextDefinition, "Attempt to edit uninitialized filter context");
697
560
  const currentFilterIndex = tab.filterContext.filterContextDefinition.filters.findIndex((item) => isDashboardAttributeFilter(item) && item.attributeFilter.localIdentifier === filterLocalId);
698
- if (!isSource && currentFilterIndex < 0) {
699
- return;
700
- }
701
561
  invariant(currentFilterIndex >= 0, "Attempt to clear selection of a non-existing filter");
702
562
  const currentFilter = tab.filterContext.filterContextDefinition.filters[currentFilterIndex];
703
563
  currentFilter.attributeFilter.negativeSelection =
@@ -710,17 +570,15 @@ function clearAttributeFilterSelectionOnTab(tab, filterLocalId, isSource) {
710
570
  * Changes the display form for the filter given by its local identifier.
711
571
  */
712
572
  const changeAttributeDisplayForm = (state, action) => {
713
- const targetTab = getTabOrActive(state, action.payload.tabLocalIdentifier);
714
- if (!targetTab) {
573
+ const target = getTabFilterContext(state, action.payload.tabLocalIdentifier);
574
+ if (!target) {
715
575
  return;
716
576
  }
717
- if (!targetTab.filterContext) {
718
- targetTab.filterContext = { ...filterContextInitialState };
719
- }
720
577
  const { filterLocalId, displayForm, isWorkingSelectionChange, enableImmediateAttributeFilterDisplayAsLabelMigration, isResultOfMigration, } = action.payload;
578
+ const { filterContext } = target;
721
579
  const filterContextDefinition = isWorkingSelectionChange
722
- ? targetTab.filterContext.workingFilterContextDefinition
723
- : targetTab.filterContext.filterContextDefinition;
580
+ ? filterContext.workingFilterContextDefinition
581
+ : filterContext.filterContextDefinition;
724
582
  invariant(filterContextDefinition?.filters, "Attempt to edit uninitialized filter context");
725
583
  const currentFilterIndex = filterContextDefinition.filters.findIndex((item) => isDashboardAttributeFilter(item) && item.attributeFilter.localIdentifier === filterLocalId);
726
584
  invariant(currentFilterIndex >= 0 || isWorkingSelectionChange, "Attempt to set parent of a non-existing filter");
@@ -738,7 +596,7 @@ const changeAttributeDisplayForm = (state, action) => {
738
596
  currentFilter.attributeFilter.displayForm = { ...displayForm };
739
597
  // update original filters to not show "reset filters" button, that will revert filter to wrong state
740
598
  if (enableImmediateAttributeFilterDisplayAsLabelMigration && isResultOfMigration) {
741
- const originalFilter = targetTab.filterContext.originalFilterContextDefinition?.filters.find((item) => isDashboardAttributeFilter(item) && item.attributeFilter.localIdentifier === filterLocalId);
599
+ const originalFilter = filterContext.originalFilterContextDefinition?.filters.find((item) => isDashboardAttributeFilter(item) && item.attributeFilter.localIdentifier === filterLocalId);
742
600
  if (isDashboardAttributeFilter(originalFilter)) {
743
601
  originalFilter.attributeFilter.displayForm = { ...displayForm };
744
602
  }
@@ -751,21 +609,12 @@ const changeAttributeDisplayForm = (state, action) => {
751
609
  */
752
610
  const changeTextFilterDisplayForm = (state, action) => {
753
611
  const targetTab = getTabOrActive(state, action.payload.tabLocalIdentifier);
754
- if (!targetTab?.filterContext) {
755
- return;
756
- }
757
- const { filterLocalId, displayForm } = action.payload;
758
- const filterContextDefinition = targetTab.filterContext.filterContextDefinition;
612
+ const filterContextDefinition = targetTab?.filterContext?.filterContextDefinition;
759
613
  if (!filterContextDefinition?.filters) {
760
614
  return;
761
615
  }
762
- const currentFilterIndex = filterContextDefinition.filters.findIndex((item) => isDashboardAttributeFilterItem(item) &&
763
- !isDashboardAttributeFilter(item) &&
764
- dashboardAttributeFilterItemLocalIdentifier(item) === filterLocalId);
765
- if (currentFilterIndex < 0) {
766
- return;
767
- }
768
- const filter = filterContextDefinition.filters[currentFilterIndex];
616
+ const { filterLocalId, displayForm } = action.payload;
617
+ const filter = findAttributeFilterItem(filterContextDefinition, filterLocalId);
769
618
  if (isDashboardArbitraryAttributeFilter(filter)) {
770
619
  filter.arbitraryAttributeFilter.displayForm = { ...displayForm };
771
620
  }
@@ -777,17 +626,12 @@ const changeTextFilterDisplayForm = (state, action) => {
777
626
  * Changes the title for the filter given by its local identifier.
778
627
  */
779
628
  const changeAttributeTitle = (state, action) => {
780
- const activeTab = getActiveTab(state);
781
- if (!activeTab) {
629
+ const target = getEditableFilterContext(state, action.payload.tabLocalIdentifier);
630
+ if (!target) {
782
631
  return;
783
632
  }
784
- if (!activeTab.filterContext) {
785
- activeTab.filterContext = { ...filterContextInitialState };
786
- }
787
- invariant(activeTab.filterContext.filterContextDefinition, "Attempt to edit uninitialized filter context");
788
633
  const { filterLocalId, title } = action.payload;
789
- const findFilter = activeTab.filterContext.filterContextDefinition.filters.find((item) => isDashboardAttributeFilterItem(item) &&
790
- dashboardAttributeFilterItemLocalIdentifier(item) === filterLocalId);
634
+ const findFilter = findAttributeFilterItem(target.definition, filterLocalId);
791
635
  invariant(findFilter, "Attempt to change title of a non-existing filter");
792
636
  if (isDashboardAttributeFilter(findFilter)) {
793
637
  findFilter.attributeFilter.title = title;
@@ -803,17 +647,12 @@ const changeAttributeTitle = (state, action) => {
803
647
  * Changes the selection mode for the filter given by its local identifier.
804
648
  */
805
649
  const changeSelectionMode = (state, action) => {
806
- const activeTab = getActiveTab(state);
807
- if (!activeTab) {
650
+ const target = getEditableFilterContext(state, action.payload.tabLocalIdentifier);
651
+ if (!target) {
808
652
  return;
809
653
  }
810
- if (!activeTab.filterContext) {
811
- activeTab.filterContext = { ...filterContextInitialState };
812
- }
813
- invariant(activeTab.filterContext.filterContextDefinition, "Attempt to edit uninitialized filter context");
814
654
  const { filterLocalId, selectionMode } = action.payload;
815
- const findFilter = activeTab.filterContext.filterContextDefinition.filters.find((item) => isDashboardAttributeFilterItem(item) &&
816
- dashboardAttributeFilterItemLocalIdentifier(item) === filterLocalId);
655
+ const findFilter = findAttributeFilterItem(target.definition, filterLocalId);
817
656
  invariant(findFilter, "Attempt to change selection mode of a non-existing filter");
818
657
  // Text filters (arbitrary, match) do not have selectionMode; no-op for them
819
658
  if (isDashboardAttributeFilter(findFilter)) {
@@ -821,15 +660,12 @@ const changeSelectionMode = (state, action) => {
821
660
  }
822
661
  };
823
662
  const addDateFilter = (state, action) => {
824
- const activeTab = getActiveTab(state);
825
- if (!activeTab) {
663
+ const target = getEditableFilterContext(state, action.payload.tabLocalIdentifier);
664
+ if (!target) {
826
665
  return;
827
666
  }
828
- if (!activeTab.filterContext) {
829
- activeTab.filterContext = { ...filterContextInitialState };
830
- }
831
- invariant(activeTab.filterContext.filterContextDefinition, "Attempt to edit uninitialized filter context");
832
667
  const { index, dateDataset } = action.payload;
668
+ const { definition } = target;
833
669
  const filter = {
834
670
  dateFilter: {
835
671
  dataSet: dateDataset,
@@ -838,73 +674,58 @@ const addDateFilter = (state, action) => {
838
674
  },
839
675
  };
840
676
  // Only draggable filters are indexed, if DateFilter is present should be always first item
841
- const isCommonDateFilterPresent = activeTab.filterContext.filterContextDefinition.filters.findIndex(isDashboardCommonDateFilter) >= 0;
677
+ const isCommonDateFilterPresent = definition.filters.findIndex(isDashboardCommonDateFilter) >= 0;
842
678
  if (index === -1) {
843
- activeTab.filterContext.filterContextDefinition.filters.push(filter);
679
+ definition.filters.push(filter);
844
680
  }
845
681
  else {
846
682
  // If CommonDateFilter is present we have to move index by 1 because index of filter is calculated just for AttributeFilers array
847
683
  const newFilterIndex = isCommonDateFilterPresent ? index + 1 : index;
848
- activeTab.filterContext.filterContextDefinition.filters.splice(newFilterIndex, 0, filter);
684
+ definition.filters.splice(newFilterIndex, 0, filter);
849
685
  }
850
686
  };
851
- //
852
- //
853
- //
854
687
  const addMeasureValueFilter = (state, action) => {
855
- // A new measure value filter has no counterpart yet. Creating one on other tabs would be a
856
- // phantom filter, so the add stays on the source tab even when sync is on.
857
- const activeTab = getTabsToMutate(state)[0];
858
- if (!activeTab) {
688
+ const target = getEditableFilterContext(state, action.payload.tabLocalIdentifier);
689
+ if (!target) {
859
690
  return;
860
691
  }
861
- if (!activeTab.filterContext) {
862
- activeTab.filterContext = { ...filterContextInitialState };
863
- }
864
- invariant(activeTab.filterContext.filterContextDefinition, "Attempt to edit uninitialized filter context");
865
692
  const { index, measure, localIdentifier, title } = action.payload;
693
+ const { definition } = target;
866
694
  const filter = {
867
695
  dashboardMeasureValueFilter: {
868
696
  measure,
869
- localIdentifier: localIdentifier ?? generateMeasureValueFilterLocalIdentifier(measure, Math.max(0, index)),
697
+ localIdentifier: resolveFilterLocalIdentifier(definition, (candidateIndex) => generateMeasureValueFilterLocalIdentifier(measure, candidateIndex), index, localIdentifier),
870
698
  title,
871
699
  },
872
700
  };
873
- const isCommonDateFilterPresent = activeTab.filterContext.filterContextDefinition.filters.findIndex(isDashboardCommonDateFilter) >= 0;
701
+ const isCommonDateFilterPresent = definition.filters.findIndex(isDashboardCommonDateFilter) >= 0;
874
702
  if (index === -1) {
875
- activeTab.filterContext.filterContextDefinition.filters.push(filter);
703
+ definition.filters.push(filter);
876
704
  }
877
705
  else {
878
706
  const newFilterIndex = isCommonDateFilterPresent ? index + 1 : index;
879
- activeTab.filterContext.filterContextDefinition.filters.splice(newFilterIndex, 0, filter);
707
+ definition.filters.splice(newFilterIndex, 0, filter);
880
708
  }
881
709
  };
882
710
  const removeMeasureValueFilter = (state, action) => {
883
- const activeTab = getActiveTab(state);
884
- if (!activeTab) {
711
+ const target = getEditableFilterContext(state, action.payload.tabLocalIdentifier);
712
+ if (!target) {
885
713
  return;
886
714
  }
887
- if (!activeTab.filterContext) {
888
- activeTab.filterContext = { ...filterContextInitialState };
889
- }
890
- invariant(activeTab.filterContext.filterContextDefinition, "Attempt to edit uninitialized filter context");
891
- activeTab.filterContext.filterContextDefinition = {
892
- ...activeTab.filterContext.filterContextDefinition,
893
- filters: activeTab.filterContext.filterContextDefinition.filters.filter((item) => !(isDashboardMeasureValueFilter(item) &&
894
- dashboardFilterLocalIdentifier(item) === action.payload.localIdentifier)),
715
+ const { localIdentifier } = action.payload;
716
+ target.filterContext.filterContextDefinition = {
717
+ ...target.definition,
718
+ filters: target.definition.filters.filter((item) => !(isDashboardMeasureValueFilter(item) &&
719
+ dashboardFilterLocalIdentifier(item) === localIdentifier)),
895
720
  };
896
721
  };
897
722
  const moveMeasureValueFilter = (state, action) => {
898
- const activeTab = getActiveTab(state);
899
- if (!activeTab) {
723
+ const target = getEditableFilterContext(state, action.payload.tabLocalIdentifier);
724
+ if (!target) {
900
725
  return;
901
726
  }
902
- if (!activeTab.filterContext) {
903
- activeTab.filterContext = { ...filterContextInitialState };
904
- }
905
- invariant(activeTab.filterContext.filterContextDefinition, "Attempt to edit uninitialized filter context");
906
727
  const { localIdentifier, index } = action.payload;
907
- const filters = activeTab.filterContext.filterContextDefinition.filters;
728
+ const filters = target.definition.filters;
908
729
  const currentFilterIndex = filters.findIndex((item) => isDashboardMeasureValueFilter(item) && dashboardFilterLocalIdentifier(item) === localIdentifier);
909
730
  invariant(currentFilterIndex >= 0, "Attempt to move non-existing measure value filter");
910
731
  const filter = filters[currentFilterIndex];
@@ -919,61 +740,91 @@ const moveMeasureValueFilter = (state, action) => {
919
740
  }
920
741
  };
921
742
  const removeDateFilter = (state, action) => {
922
- const activeTab = getActiveTab(state);
923
- if (!activeTab) {
743
+ const target = getEditableFilterContext(state, action.payload.tabLocalIdentifier);
744
+ if (!target) {
924
745
  return;
925
746
  }
926
- if (!activeTab.filterContext) {
927
- activeTab.filterContext = { ...filterContextInitialState };
928
- }
929
- invariant(activeTab.filterContext.filterContextDefinition, "Attempt to edit uninitialized filter context");
930
747
  const { dataSet } = action.payload;
931
- const newFilters = activeTab.filterContext.filterContextDefinition.filters.filter((item) => !isDashboardDateFilter(item) || !areObjRefsEqual(item.dateFilter.dataSet, dataSet));
932
- activeTab.filterContext.filterContextDefinition = {
933
- ...activeTab.filterContext.filterContextDefinition,
934
- filters: newFilters,
748
+ const { filterContext, definition } = target;
749
+ const removed = definition.filters.find((item) => isDashboardDateFilterWithDimension(item) && areObjRefsEqual(item.dateFilter.dataSet, dataSet));
750
+ const removedLocalIdentifier = isDashboardDateFilter(removed)
751
+ ? removed.dateFilter.localIdentifier
752
+ : undefined;
753
+ filterContext.filterContextDefinition = {
754
+ ...definition,
755
+ filters: definition.filters.filter((item) => !isDashboardDateFilter(item) || !areObjRefsEqual(item.dateFilter.dataSet, dataSet)),
935
756
  };
757
+ removeDimensionDateFilterDependencies(filterContext.filterContextDefinition, dataSet, removedLocalIdentifier);
936
758
  };
759
+ /**
760
+ * Drops attribute-filter dependencies that point at the removed dimension date filter, including
761
+ * filters that exist only on this tab. Matches both the filter local identifier and the legacy
762
+ * dataset-identifier form.
763
+ */
764
+ function removeDimensionDateFilterDependencies(definition, dataSet, removedLocalIdentifier) {
765
+ const datasetIdentifier = isIdentifierRef(dataSet) ? dataSet.identifier : undefined;
766
+ for (const item of definition.filters) {
767
+ if (isDashboardAttributeFilter(item)) {
768
+ const next = withoutRemovedDateDependency(item.attributeFilter.filterElementsByDate, removedLocalIdentifier, datasetIdentifier);
769
+ if (next !== item.attributeFilter.filterElementsByDate) {
770
+ item.attributeFilter.filterElementsByDate = next;
771
+ }
772
+ }
773
+ else if (isDashboardArbitraryAttributeFilter(item)) {
774
+ const next = withoutRemovedDateDependency(item.arbitraryAttributeFilter.filterElementsByDate, removedLocalIdentifier, datasetIdentifier);
775
+ if (next !== item.arbitraryAttributeFilter.filterElementsByDate) {
776
+ item.arbitraryAttributeFilter.filterElementsByDate = next;
777
+ }
778
+ }
779
+ }
780
+ }
781
+ function withoutRemovedDateDependency(dependencies, removedLocalIdentifier, datasetIdentifier) {
782
+ if (!dependencies?.length) {
783
+ return dependencies;
784
+ }
785
+ const remaining = dependencies.filter((dependency) => {
786
+ if (dependency.isCommonDate) {
787
+ return true;
788
+ }
789
+ const matchesLocalIdentifier = removedLocalIdentifier !== undefined &&
790
+ dependency.filterLocalIdentifier === removedLocalIdentifier;
791
+ const matchesDatasetIdentifier = datasetIdentifier !== undefined && dependency.filterLocalIdentifier === datasetIdentifier;
792
+ return !matchesLocalIdentifier && !matchesDatasetIdentifier;
793
+ });
794
+ return remaining.length === dependencies.length ? dependencies : remaining;
795
+ }
937
796
  const moveDateFilter = (state, action) => {
938
- const activeTab = getActiveTab(state);
939
- if (!activeTab) {
797
+ const target = getEditableFilterContext(state, action.payload.tabLocalIdentifier);
798
+ if (!target) {
940
799
  return;
941
800
  }
942
- if (!activeTab.filterContext) {
943
- activeTab.filterContext = { ...filterContextInitialState };
944
- }
945
- invariant(activeTab.filterContext.filterContextDefinition, "Attempt to edit uninitialized filter context");
946
801
  const { dataSet, index } = action.payload;
947
- const currentFilterIndex = activeTab.filterContext.filterContextDefinition.filters.findIndex((item) => isDashboardDateFilter(item) && areObjRefsEqual(item.dateFilter.dataSet, dataSet));
802
+ const filters = target.definition.filters;
803
+ const currentFilterIndex = filters.findIndex((item) => isDashboardDateFilter(item) && areObjRefsEqual(item.dateFilter.dataSet, dataSet));
948
804
  invariant(currentFilterIndex >= 0, "Attempt to move non-existing filter");
949
- const filter = activeTab.filterContext.filterContextDefinition.filters[currentFilterIndex];
950
- activeTab.filterContext.filterContextDefinition.filters.splice(currentFilterIndex, 1);
805
+ const filter = filters[currentFilterIndex];
806
+ filters.splice(currentFilterIndex, 1);
951
807
  // Filters are indexed just for attribute filters, if DateFilter is present should be always first item
952
- const isCommonDateFilterPresent = activeTab.filterContext.filterContextDefinition.filters.findIndex(isDashboardCommonDateFilter) >= 0;
808
+ const isCommonDateFilterPresent = filters.findIndex(isDashboardCommonDateFilter) >= 0;
953
809
  if (index === -1) {
954
- activeTab.filterContext.filterContextDefinition.filters.push(filter);
810
+ filters.push(filter);
955
811
  }
956
812
  else {
957
813
  // If DateFilter is present we have to move index by 1 because index of filter is calculated just for DraggableFilters array
958
814
  const dateFilterIndex = isCommonDateFilterPresent ? index + 1 : index;
959
- activeTab.filterContext.filterContextDefinition.filters.splice(dateFilterIndex, 0, filter);
815
+ filters.splice(dateFilterIndex, 0, filter);
960
816
  }
961
817
  };
962
818
  /**
963
819
  * Changes the element limiting items for the filter given by its local identifier.
964
820
  */
965
821
  const changeLimitingItems = (state, action) => {
966
- const activeTab = getActiveTab(state);
967
- if (!activeTab) {
822
+ const target = getEditableFilterContext(state, action.payload.tabLocalIdentifier);
823
+ if (!target) {
968
824
  return;
969
825
  }
970
- if (!activeTab.filterContext) {
971
- activeTab.filterContext = { ...filterContextInitialState };
972
- }
973
- invariant(activeTab.filterContext.filterContextDefinition, "Attempt to edit uninitialized filter context");
974
826
  const { filterLocalId, limitingItems } = action.payload;
975
- const findFilter = activeTab.filterContext.filterContextDefinition.filters.find((item) => isDashboardAttributeFilterItem(item) &&
976
- dashboardAttributeFilterItemLocalIdentifier(item) === filterLocalId);
827
+ const findFilter = findAttributeFilterItem(target.definition, filterLocalId);
977
828
  invariant(findFilter, "Attempt to change limiting items of a non-existing filter");
978
829
  if (isDashboardAttributeFilter(findFilter)) {
979
830
  findFilter.attributeFilter.validateElementsBy = limitingItems;
@@ -1012,8 +863,9 @@ export function clearFilterContextWorkingValues(tab) {
1012
863
  tab.filterContext.workingFilterContextDefinition = { filters: [] };
1013
864
  }
1014
865
  const resetWorkingFilterSelection = (state) => {
1015
- for (const tab of getTabsToMutate(state)) {
1016
- clearFilterContextWorkingValues(tab);
866
+ const activeTab = getActiveTab(state);
867
+ if (activeTab) {
868
+ clearFilterContextWorkingValues(activeTab);
1017
869
  }
1018
870
  };
1019
871
  //
@@ -1030,59 +882,24 @@ const setDefaultFilterOverrides = (state, action) => {
1030
882
  activeTab.filterContext.defaultFilterOverrides = action.payload;
1031
883
  };
1032
884
  const changeMeasureValueFilterCondition = (state, action) => {
1033
- const { localIdentifier, isWorkingSelectionChange, enablePersistentFiltersAcrossTabs } = action.payload;
1034
- const tabs = getTabsToMutate(state, undefined, enablePersistentFiltersAcrossTabs, isWorkingSelectionChange);
1035
- const source = tabs[0];
1036
- if (!source) {
1037
- return;
1038
- }
1039
- const sourceFilter = findAppliedFilterByLocalId(source, localIdentifier);
1040
- changeMeasureValueFilterConditionOnTab(source, action.payload, localIdentifier, true);
1041
- if (!sourceFilter || !isDashboardMeasureValueFilter(sourceFilter)) {
885
+ const tab = getTabOrActive(state, action.payload.tabLocalIdentifier);
886
+ if (!tab) {
1042
887
  return;
1043
888
  }
1044
- for (const tab of tabs.slice(1)) {
1045
- const match = findCounterpartFilter(source, sourceFilter, tab, state.attributesWithReferences);
1046
- const targetLocalId = match ? dashboardFilterLocalIdentifier(match) : undefined;
1047
- if (!targetLocalId || !match) {
1048
- continue;
1049
- }
1050
- if (action.payload.restoreTargetOriginal) {
1051
- const original = findOriginalFilter(tab, match);
1052
- if (!isDashboardMeasureValueFilter(original)) {
1053
- continue;
1054
- }
1055
- changeMeasureValueFilterConditionOnTab(tab, {
1056
- ...action.payload,
1057
- localIdentifier: targetLocalId,
1058
- conditions: original.dashboardMeasureValueFilter.conditions,
1059
- }, targetLocalId, false);
1060
- continue;
1061
- }
1062
- changeMeasureValueFilterConditionOnTab(tab, action.payload, targetLocalId, false);
1063
- }
889
+ changeMeasureValueFilterConditionOnTab(tab, action.payload);
1064
890
  };
1065
- function changeMeasureValueFilterConditionOnTab(tab, payload, localIdentifier, isSource) {
891
+ function changeMeasureValueFilterConditionOnTab(tab, payload) {
1066
892
  if (!tab.filterContext) {
1067
- if (!isSource) {
1068
- return;
1069
- }
1070
893
  tab.filterContext = { ...filterContextInitialState };
1071
894
  }
1072
- const { conditions, isWorkingSelectionChange } = payload;
895
+ const { localIdentifier, conditions, isWorkingSelectionChange } = payload;
1073
896
  const filterContextDefinition = isWorkingSelectionChange
1074
897
  ? tab.filterContext?.workingFilterContextDefinition
1075
898
  : tab.filterContext?.filterContextDefinition;
1076
- if (!isSource && !filterContextDefinition?.filters) {
1077
- return;
1078
- }
1079
899
  invariant(filterContextDefinition?.filters, "Attempt to edit uninitialized filter context");
1080
900
  const filters = filterContextDefinition.filters;
1081
901
  const index = filters.findIndex((item) => isDashboardMeasureValueFilter(item) && dashboardFilterLocalIdentifier(item) === localIdentifier);
1082
902
  const hasConditions = !!conditions && conditions.length > 0;
1083
- if (!isSource && index < 0) {
1084
- return;
1085
- }
1086
903
  invariant(index >= 0 || isWorkingSelectionChange, `Attempt to change condition of measure value filter ${localIdentifier} that does not exist`);
1087
904
  if (isWorkingSelectionChange && index < 0) {
1088
905
  const workingFilter = {
@@ -1103,48 +920,17 @@ function changeMeasureValueFilterConditionOnTab(tab, payload, localIdentifier, i
1103
920
  };
1104
921
  }
1105
922
  const setMeasureValueFilterDimensionality = (state, action) => {
1106
- const { localIdentifier, restoreTargetOriginal, enablePersistentFiltersAcrossTabs } = action.payload;
1107
- const tabs = getTabsToMutate(state, undefined, enablePersistentFiltersAcrossTabs);
1108
- const source = tabs[0];
1109
- if (!source) {
1110
- return;
1111
- }
1112
- const sourceFilter = findAppliedFilterByLocalId(source, localIdentifier);
1113
- setMeasureValueFilterDimensionalityOnTab(source, action.payload, localIdentifier, true);
1114
- if (!sourceFilter || !isDashboardMeasureValueFilter(sourceFilter)) {
923
+ const tab = getTabOrActive(state, action.payload.tabLocalIdentifier);
924
+ if (!tab) {
1115
925
  return;
1116
926
  }
1117
- for (const tab of tabs.slice(1)) {
1118
- const match = findCounterpartFilter(source, sourceFilter, tab, state.attributesWithReferences);
1119
- const targetLocalId = match ? dashboardFilterLocalIdentifier(match) : undefined;
1120
- if (!targetLocalId || !match) {
1121
- continue;
1122
- }
1123
- if (restoreTargetOriginal) {
1124
- const original = findOriginalFilter(tab, match);
1125
- if (!isDashboardMeasureValueFilter(original)) {
1126
- continue;
1127
- }
1128
- setMeasureValueFilterDimensionalityOnTab(tab, {
1129
- ...action.payload,
1130
- localIdentifier: targetLocalId,
1131
- dimensionality: original.dashboardMeasureValueFilter.dimensionality,
1132
- }, targetLocalId, false);
1133
- continue;
1134
- }
1135
- setMeasureValueFilterDimensionalityOnTab(tab, action.payload, targetLocalId, false);
1136
- }
927
+ setMeasureValueFilterDimensionalityOnTab(tab, action.payload);
1137
928
  };
1138
- function setMeasureValueFilterDimensionalityOnTab(tab, payload, localIdentifier, isSource) {
929
+ function setMeasureValueFilterDimensionalityOnTab(tab, payload) {
930
+ const { localIdentifier } = payload;
1139
931
  const filters = tab.filterContext?.filterContextDefinition?.filters;
1140
- if (!isSource && !filters) {
1141
- return;
1142
- }
1143
932
  invariant(filters, "Attempt to edit uninitialized filter context");
1144
933
  const index = filters.findIndex((item) => isDashboardMeasureValueFilter(item) && dashboardFilterLocalIdentifier(item) === localIdentifier);
1145
- if (!isSource && index < 0) {
1146
- return;
1147
- }
1148
934
  invariant(index >= 0, `Attempt to change dimensionality of measure value filter ${localIdentifier} that does not exist`);
1149
935
  const filter = filters[index];
1150
936
  const dimensionality = payload.dimensionality && payload.dimensionality.length > 0 ? payload.dimensionality : undefined;
@@ -1156,23 +942,96 @@ function setMeasureValueFilterDimensionalityOnTab(tab, payload, localIdentifier,
1156
942
  };
1157
943
  }
1158
944
  const changeMeasureValueFilterTitle = (state, action) => {
1159
- const activeTab = getActiveTab(state);
1160
- if (!activeTab) {
945
+ const { filterLocalId, title, tabLocalIdentifier } = action.payload;
946
+ const targetTab = getTabOrActive(state, tabLocalIdentifier);
947
+ if (!targetTab) {
1161
948
  return;
1162
949
  }
1163
- invariant(activeTab.filterContext?.filterContextDefinition?.filters, "Attempt to edit uninitialized filter context");
1164
- const filters = activeTab.filterContext.filterContextDefinition.filters;
1165
- const index = filters.findIndex((item) => isDashboardMeasureValueFilter(item) &&
1166
- dashboardFilterLocalIdentifier(item) === action.payload.filterLocalId);
1167
- invariant(index >= 0, `Attempt to change title of measure value filter ${action.payload.filterLocalId} that does not exist`);
950
+ const filters = targetTab.filterContext?.filterContextDefinition?.filters;
951
+ invariant(filters, "Attempt to edit uninitialized filter context");
952
+ const index = filters.findIndex((item) => isDashboardMeasureValueFilter(item) && dashboardFilterLocalIdentifier(item) === filterLocalId);
953
+ invariant(index >= 0, `Attempt to change title of measure value filter ${filterLocalId} that does not exist`);
1168
954
  const filter = filters[index];
1169
955
  filters[index] = {
1170
956
  dashboardMeasureValueFilter: {
1171
957
  ...filter.dashboardMeasureValueFilter,
1172
- title: action.payload.title,
958
+ title,
1173
959
  },
1174
960
  };
1175
961
  };
962
+ function isWrittenFilter(filter, written) {
963
+ if (isDashboardDateFilter(filter)) {
964
+ const { dataSet } = filter.dateFilter;
965
+ return dataSet
966
+ ? written.dateDataSets.some((writtenDataSet) => areObjRefsEqual(writtenDataSet, dataSet))
967
+ : written.commonDateFilter;
968
+ }
969
+ const localIdentifier = dashboardFilterLocalIdentifier(filter);
970
+ return !!localIdentifier && written.filterLocalIds.includes(localIdentifier);
971
+ }
972
+ /**
973
+ * Makes the applied values of the written filters their initial values. A default filter view that
974
+ * initializes the shared state is that initial state, so the reset button stays hidden until the user
975
+ * changes it. Filters the view did not write keep their initial values, so a change the user made
976
+ * before stays resettable.
977
+ */
978
+ const adoptAppliedFiltersAsOriginal = (state, action) => {
979
+ for (const written of action.payload) {
980
+ const tab = getTabOrActive(state, written.tabLocalIdentifier);
981
+ const definition = tab?.filterContext?.filterContextDefinition;
982
+ if (!tab?.filterContext || !definition) {
983
+ continue;
984
+ }
985
+ const originals = tab.filterContext.originalFilterContextDefinition?.filters ?? [];
986
+ const originalOf = new Map();
987
+ const matchedOriginals = new Set();
988
+ const match = (filter, original) => {
989
+ if (original && !matchedOriginals.has(original)) {
990
+ originalOf.set(filter, original);
991
+ matchedOriginals.add(original);
992
+ }
993
+ };
994
+ // Each initial value belongs to one applied filter. Exact localIdentifier matches go first, so a
995
+ // filter without an initial value, e.g. a cross-filtering one, cannot take the initial value of
996
+ // another filter with the same display form through identity matching.
997
+ for (const filter of definition.filters) {
998
+ const localIdentifier = dashboardFilterLocalIdentifier(filter);
999
+ match(filter, localIdentifier
1000
+ ? originals.find((original) => dashboardFilterLocalIdentifier(original) === localIdentifier)
1001
+ : undefined);
1002
+ }
1003
+ for (const filter of definition.filters) {
1004
+ if (!originalOf.has(filter)) {
1005
+ match(filter, findOriginalFilter(tab, filter));
1006
+ }
1007
+ }
1008
+ // the reset button compares the lists in order, so the applied order is kept; an unwritten filter
1009
+ // without an initial value, e.g. a cross-filtering one, stays out of the initial values
1010
+ const adopted = definition.filters.flatMap((filter) => {
1011
+ if (isWrittenFilter(filter, written)) {
1012
+ return [cloneDeep(filter)];
1013
+ }
1014
+ const original = originalOf.get(filter);
1015
+ return original ? [cloneDeep(original)] : [];
1016
+ });
1017
+ // an unwritten initial value without an applied filter, e.g. a common date filter set to all time
1018
+ for (const original of originals) {
1019
+ if (matchedOriginals.has(original) || isWrittenFilter(original, written)) {
1020
+ continue;
1021
+ }
1022
+ if (isDashboardCommonDateFilter(original)) {
1023
+ adopted.unshift(cloneDeep(original));
1024
+ }
1025
+ else {
1026
+ adopted.push(cloneDeep(original));
1027
+ }
1028
+ }
1029
+ tab.filterContext.originalFilterContextDefinition = {
1030
+ ...(tab.filterContext.originalFilterContextDefinition ?? definition),
1031
+ filters: adopted,
1032
+ };
1033
+ }
1034
+ };
1176
1035
  //
1177
1036
  //
1178
1037
  //
@@ -1209,4 +1068,5 @@ export const filterContextReducers = {
1209
1068
  changeMeasureValueFilterCondition,
1210
1069
  setMeasureValueFilterDimensionality,
1211
1070
  changeMeasureValueFilterTitle,
1071
+ adoptAppliedFiltersAsOriginal,
1212
1072
  };