@gooddata/sdk-ui-ext 11.55.0-alpha.2 → 11.55.0-alpha.4

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 (68) hide show
  1. package/NOTICE +9 -9
  2. package/esm/index.d.ts +4 -2
  3. package/esm/index.d.ts.map +1 -1
  4. package/esm/index.js +3 -2
  5. package/esm/internal/components/configurationControls/conditionalFormatting/ConditionEditor.d.ts.map +1 -1
  6. package/esm/internal/components/configurationControls/conditionalFormatting/ConditionEditor.js +8 -5
  7. package/esm/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingDialog.d.ts +20 -5
  8. package/esm/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingDialog.d.ts.map +1 -1
  9. package/esm/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingDialog.js +143 -33
  10. package/esm/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingSection.d.ts +19 -1
  11. package/esm/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingSection.d.ts.map +1 -1
  12. package/esm/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingSection.js +146 -46
  13. package/esm/internal/components/configurationControls/conditionalFormatting/conditionalFormattingModel.d.ts +34 -7
  14. package/esm/internal/components/configurationControls/conditionalFormatting/conditionalFormattingModel.d.ts.map +1 -1
  15. package/esm/internal/components/configurationControls/conditionalFormatting/conditionalFormattingModel.js +38 -7
  16. package/esm/internal/components/configurationPanels/PivotTableConfigurationPanel.d.ts.map +1 -1
  17. package/esm/internal/components/configurationPanels/PivotTableConfigurationPanel.js +9 -4
  18. package/esm/internal/components/pluggableVisualizations/pivotTableNext/PluggablePivotTableNext.d.ts.map +1 -1
  19. package/esm/internal/components/pluggableVisualizations/pivotTableNext/PluggablePivotTableNext.js +18 -18
  20. package/esm/internal/components/pluggableVisualizations/pivotTableNext/pivotTableNextConfigFromInsight.d.ts +1 -2
  21. package/esm/internal/components/pluggableVisualizations/pivotTableNext/pivotTableNextConfigFromInsight.d.ts.map +1 -1
  22. package/esm/internal/components/pluggableVisualizations/pivotTableNext/pivotTableNextConfigFromInsight.js +3 -9
  23. package/esm/internal/index.d.ts +1 -0
  24. package/esm/internal/index.d.ts.map +1 -1
  25. package/esm/internal/index.js +1 -0
  26. package/esm/internal/translations/de-DE.localization-bundle.js +2 -2
  27. package/esm/internal/translations/en-US.localization-bundle.d.ts +14 -2
  28. package/esm/internal/translations/en-US.localization-bundle.d.ts.map +1 -1
  29. package/esm/internal/translations/en-US.localization-bundle.js +18 -6
  30. package/esm/internal/translations/zh-HK.localization-bundle.js +40 -40
  31. package/esm/internal/translations/zh-Hant.localization-bundle.js +1 -1
  32. package/esm/internal/utils/embeddingInsightViewCodeGenerator/insightViewConfig.d.ts.map +1 -1
  33. package/esm/internal/utils/embeddingInsightViewCodeGenerator/insightViewConfig.js +2 -1
  34. package/esm/internal/utils/propertiesHelper.d.ts +33 -1
  35. package/esm/internal/utils/propertiesHelper.d.ts.map +1 -1
  36. package/esm/internal/utils/propertiesHelper.js +43 -1
  37. package/esm/locales.d.ts +11 -2
  38. package/esm/locales.d.ts.map +1 -1
  39. package/esm/locales.js +5 -2
  40. package/esm/sdk-ui-ext.d.ts +194 -4
  41. package/esm/share/ObjectShareDialog.d.ts +19 -0
  42. package/esm/share/ObjectShareDialog.d.ts.map +1 -1
  43. package/esm/share/ObjectShareDialog.js +8 -1
  44. package/esm/share/objectShareController.helpers.d.ts +8 -52
  45. package/esm/share/objectShareController.helpers.d.ts.map +1 -1
  46. package/esm/share/objectShareController.helpers.js +23 -0
  47. package/esm/share/objectShareController.types.d.ts +76 -3
  48. package/esm/share/objectShareController.types.d.ts.map +1 -1
  49. package/esm/share/useAccessList.d.ts +14 -3
  50. package/esm/share/useAccessList.d.ts.map +1 -1
  51. package/esm/share/useAccessList.js +26 -7
  52. package/esm/share/useApplyObjectPermissions.d.ts +13 -0
  53. package/esm/share/useApplyObjectPermissions.d.ts.map +1 -0
  54. package/esm/share/useApplyObjectPermissions.js +33 -0
  55. package/esm/share/useObjectShareController.d.ts.map +1 -1
  56. package/esm/share/useObjectShareController.js +20 -9
  57. package/esm/share/useObjectShareDialog.d.ts +12 -3
  58. package/esm/share/useObjectShareDialog.d.ts.map +1 -1
  59. package/esm/share/useObjectShareDialog.js +16 -3
  60. package/package.json +23 -24
  61. package/styles/css/main.css +65 -4
  62. package/styles/css/main.css.map +1 -1
  63. package/styles/internal/css/conditional_formatting.css +38 -4
  64. package/styles/internal/css/conditional_formatting.css.map +1 -1
  65. package/styles/internal/css/insightPicker.css +27 -0
  66. package/styles/internal/css/insightPicker.css.map +1 -1
  67. package/styles/internal/scss/conditional_formatting.scss +67 -18
  68. package/styles/internal/scss/insightPicker.scss +41 -0
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // (C) 2026 GoodData Corporation
3
3
  import { useState } from "react";
4
4
  import cx from "classnames";
@@ -8,7 +8,7 @@ import { Button, UiIconButton } from "@gooddata/sdk-ui-kit";
8
8
  import { conditionalFormattingMessages } from "../../../../locales.js";
9
9
  import { ConfigSection } from "../ConfigSection.js";
10
10
  import { ConditionalFormattingDialog } from "./ConditionalFormattingDialog.js";
11
- import { buildTargetOptions, findTargetOption, isCustomTarget, isDateTarget, isRuleComplete, newRule, semanticRuleFor, targetIcon, targetLocalId, } from "./conditionalFormattingModel.js";
11
+ import { buildTargetOptions, findTargetOption, isCustomTarget, isDateTarget, isRuleComplete, isSuppressedTarget, newRule, semanticRuleFor, targetIcon, targetLocalId, targetToValue, withSuppressedTargets, } from "./conditionalFormattingModel.js";
12
12
  import { ReorderList } from "./ReorderList.js";
13
13
  import { useCfDateFilterOptions } from "./useCfDateFilterOptions.js";
14
14
  const SECTION_ID = "conditionalFormatting_section";
@@ -20,37 +20,50 @@ function RuleChip({ rule, option, invalid, editable, labels, slot, onEdit, onDel
20
20
  }
21
21
  // A held-open semantic dialog is a snapshot from when "View" was clicked — re-derive it against the
22
22
  // current semantic payload every render so it tracks edits, and disappears (rather than showing stale
23
- // or unrelated rules) once loading clears `targetData.semantic`, the target stops being Inherited (its
24
- // row left `semanticRows` because it became Custom), or its semantic rule itself is no longer present.
25
- function resolveActiveDialog(dialog, semanticMap, config) {
23
+ // or unrelated rules) once the target stops being Inherited (an authored rule appeared elsewhere,
24
+ // making the target genuinely Custom) or the target itself is no longer part of the insight at all
25
+ // (removed from its bucket — there's nothing left for a Save to even target). Deliberately does NOT
26
+ // also close it just because the semantic DEFAULT vanished while the target itself is still there
27
+ // (catalog rule removed, a data-view blip) — the dialog's own wantsDefault-recovery logic
28
+ // (ConditionalFormattingDialog.tsx) already handles that gracefully (unlocks, falls back to whatever's
29
+ // in the draft); unmounting it here first would discard an edit made after unchecking "Use default
30
+ // rule" before that logic ever runs. `targetData.semantic` is kept stable across loading transitions
31
+ // so isCustomTarget/target-presence below are the only things that fire on a re-execution, not every
32
+ // one of them.
33
+ function resolveActiveDialog(dialog, semanticMap, config, targetOptions) {
26
34
  if (dialog?.kind !== "semantic") {
27
35
  return dialog;
28
36
  }
29
37
  if (isCustomTarget(config, dialog.option.target)) {
30
38
  return null;
31
39
  }
40
+ const value = targetToValue(dialog.option.target);
41
+ if (!targetOptions.some((option) => targetToValue(option.target) === value)) {
42
+ return null;
43
+ }
32
44
  const current = semanticMap[targetLocalId(dialog.option.target)];
33
45
  return current
34
46
  ? { kind: "semantic", option: dialog.option, rule: semanticRuleFor(dialog.option, current) }
35
- : null;
47
+ : dialog;
36
48
  }
37
- // `activeDialog.rule.id` alone is stable across a semantic-rule content change (it's target-based, not
38
- // content-based, per semanticRuleFor) ConditionalFormattingDialog seeds its local draft from `rule` via
39
- // useState and won't re-sync on a prop change, so without remounting on content change too, a rule that
40
- // changes while the (read-only) dialog is open would keep showing what it looked like when opened.
49
+ // Just the (target-based, per semanticRuleFor) id no need to key on content too: the dialog is
50
+ // handed the live `semanticByTarget` map and re-derives the current semantic conditions from it every
51
+ // render (via `displayRule`), so a semantic-rule update while the dialog is open shows up without a
52
+ // remount. Keying on content as well would risk discarding an in-progress unchecked-and-editing draft
53
+ // if fresh data happened to arrive mid-edit.
41
54
  function dialogKey(dialog) {
42
- return dialog.kind === "semantic"
43
- ? `${dialog.rule.id}:${JSON.stringify(dialog.rule.conditions)}`
44
- : dialog.rule.id;
55
+ return dialog.rule.id;
45
56
  }
46
- // Read-only: no drag handle, no delete nothing on the row is editable here, only the View
47
- // button opens the (readOnly) dialog. Turning it off/customizing it is PR-6, not this one.
48
- function SemanticRuleRow({ option, viewLabel, onView }) {
49
- return (_jsxs("div", { className: "gd-cf-rule gd-cf-rule--semantic", children: [
50
- _jsx("span", { className: `gd-cf-type-icon ${targetIcon(option.target.kind, isDateTarget(option))}`, "aria-hidden": "true" }), _jsx("span", { className: "gd-cf-rule__title", children: option.title }), _jsx(Button, { className: "gd-button-link gd-cf-rule__view", value: viewLabel, onClick: onView })
51
- ] }));
57
+ // No drag handle, no delete row here the target's own dialog (opened via Edit) carries the
58
+ // "Use default rule" checkbox that switches it to Custom. The row itself only ever offers the
59
+ // on/off toggle (`suppressedTargets` suppression) and, while on, a way to open that dialog.
60
+ function SemanticRuleRow({ option, editLabel, toggleAriaLabel, off, onToggleOff, onEdit, }) {
61
+ return (_jsxs("div", { className: cx("gd-cf-rule", "gd-cf-rule--semantic", { "gd-cf-rule--off": off }), children: [
62
+ _jsxs("label", { className: "input-checkbox-toggle s-checkbox-toggle-label gd-cf-rule__toggle", children: [
63
+ _jsx("input", { "aria-label": toggleAriaLabel, type: "checkbox", checked: !off, onChange: (e) => onToggleOff(!e.target.checked), className: `s-checkbox-toggle ${off ? "s-disabled" : "s-enabled"}` }), _jsx("span", { className: "input-label-text" })
64
+ ] }), _jsx("span", { className: `gd-cf-type-icon ${targetIcon(option.target.kind, isDateTarget(option))}`, "aria-hidden": "true" }), _jsx("span", { className: "gd-cf-rule__title", children: option.title }), off ? null : (_jsx(Button, { className: "gd-button-link gd-cf-rule__view", value: editLabel, onClick: onEdit }))] }));
52
65
  }
53
- export function ConditionalFormattingSection({ properties, propertiesMeta, insight, targetData, backend, workspace, dateSettings, separators, isLoading, pushData, }) {
66
+ export function ConditionalFormattingSection({ properties, propertiesMeta, insight, targetData, backend, workspace, dateSettings, separators, isLoading, pushData, enableConditionalFormatting = true, enableSemanticConditionalFormatting = false, }) {
54
67
  const intl = useIntl();
55
68
  const [dialog, setDialog] = useState(null);
56
69
  const config = properties?.controls?.["conditionalFormatting"];
@@ -59,16 +72,36 @@ export function ConditionalFormattingSection({ properties, propertiesMeta, insig
59
72
  const targetOptions = insight ? buildTargetOptions(insight, targetData) : [];
60
73
  // Only date pickers consume the catalog — skip the backend query when no target is date-eligible.
61
74
  const dateFilterOptions = useCfDateFilterOptions(backend, workspace, targetOptions.some(isDateTarget));
62
- // Targets inheriting a semantic-layer rule, minus any already Custom (an authored rule or a
63
- // customTargets entry). NOT gated on the toggle above: the engine's `enabled` only ever
64
- // deactivates the insight's OWN authored rules — an untouched (Inherited) target keeps
65
- // painting regardless, so hiding it here would desync the panel from what's on screen.
66
75
  const semanticMap = targetData?.semantic ?? {};
67
- const semanticRows = targetOptions.flatMap((option) => {
68
- const rule = semanticMap[targetLocalId(option.target)];
69
- return !rule || isCustomTarget(config, option.target) ? [] : [{ option, semantic: rule }];
70
- });
71
- const activeDialog = resolveActiveDialog(dialog, semanticMap, config);
76
+ // Targets with a semantic-layer rule, minus any that have their own authored rule (those render
77
+ // as a RuleChip above instead). A `suppressedTargets`-suppressed target still renders here (tagged
78
+ // `off`) it needs a row to turn back on. NOT gated on the toggle above: the engine's `enabled`
79
+ // only ever deactivates the insight's OWN authored rules — an untouched (Inherited) target keeps
80
+ // painting regardless, so hiding it here would desync the panel from what's on screen. IS gated
81
+ // on `enableSemanticConditionalFormatting`, for the opposite reason: an inherited row the engine
82
+ // won't actually paint (flag off) would be the desync the paragraph above is trying to avoid. ALSO
83
+ // gated on `semanticFresh`: unlike an already-open dialog (which the user is already looking at
84
+ // and which the dialog's own recovery logic protects), a brand-new row opened from a stale map
85
+ // could let the user View/suppress/customize a rule that's about to be superseded — offer no new
86
+ // ones while the map is confirmed stale, rather than only guarding the persistence side.
87
+ const semanticRows = enableSemanticConditionalFormatting && targetData?.semanticFresh !== false
88
+ ? targetOptions.flatMap((option) => {
89
+ const rule = semanticMap[targetLocalId(option.target)];
90
+ const value = targetToValue(option.target);
91
+ // When insight-level CF is off, the engine never sees `rules` at all (the renderer
92
+ // passes it `undefined`) — every target resolves as Inherited regardless of what's
93
+ // still sitting in persisted properties, so a stale authored rule must not hide the
94
+ // semantic row here either, or the panel would show nothing for a target the table
95
+ // is actually painting.
96
+ const hasAuthoredRule = enableConditionalFormatting &&
97
+ rules.some((existing) => targetToValue(existing.target) === value);
98
+ if (!rule || hasAuthoredRule) {
99
+ return [];
100
+ }
101
+ return [{ option, semantic: rule, off: isSuppressedTarget(config, option.target) }];
102
+ })
103
+ : [];
104
+ const activeDialog = resolveActiveDialog(dialog, semanticMap, config, targetOptions);
72
105
  // `resolveActiveDialog` only hides an invalidated semantic dialog from render — without also
73
106
  // clearing the underlying state here, a later data view that happens to carry a rule for the same
74
107
  // (possibly stale) target would silently reopen it, with no new View click.
@@ -79,13 +112,48 @@ export function ConditionalFormattingSection({ properties, propertiesMeta, insig
79
112
  // it's unknown — don't flash a false "Invalid" badge, don't author a plain-text rule on a date attr.
80
113
  const targetDataReady = targetData?.dates !== undefined;
81
114
  const canAddRule = !isLoading && targetOptions.length > 0 && targetDataReady;
82
- const commit = (rulesNext, enabledNext) => {
115
+ const commit = (rulesNext, enabledNext, suppressedTargetsNext = config?.suppressedTargets) => {
116
+ // An entry with no live semantic rule behind it (the catalog's rule was itself removed or
117
+ // detached elsewhere) suppresses nothing and can never resurface in the panel to be turned
118
+ // back on — drop it opportunistically on any edit rather than leaving it to accumulate
119
+ // forever. Guarded three ways: before the first data view, an empty/absent map means "unknown
120
+ // yet", not "confirmed gone"; `semanticFresh === false` means the map still belongs to a
121
+ // superseded execution (deliberately NOT gated on `isLoading` instead, which can already read
122
+ // false before a fresh data view actually lands, or never, if that execution fails — the gap
123
+ // `semanticFresh` is specifically tracked to close, see ICfTargetData's doc); and a target
124
+ // temporarily missing from `targetOptions` itself (e.g. removed from the bucket, not from the
125
+ // catalog) is left untouched — its semantic entry being absent from THIS execution says
126
+ // nothing about whether its catalog rule still exists, only that it wasn't queried this time.
127
+ const prunedSuppressedTargets = targetData?.semanticFresh === false || targetData?.semantic === undefined
128
+ ? suppressedTargetsNext
129
+ : suppressedTargetsNext?.filter((target) => {
130
+ const value = targetToValue(target);
131
+ const inCurrentExecution = targetOptions.some((option) => targetToValue(option.target) === value);
132
+ return !inCurrentExecution || semanticMap[targetLocalId(target)] !== undefined;
133
+ });
83
134
  // Spread the existing config so version (and any future cross-stack fields) survive an edit.
84
- const next = { ...config, enabled: enabledNext, rules: rulesNext };
135
+ const next = withSuppressedTargets({ ...config, enabled: enabledNext, rules: rulesNext }, prunedSuppressedTargets);
85
136
  const cloned = cloneDeep(properties ?? {});
86
137
  set(cloned, "controls.conditionalFormatting", next);
87
138
  pushData?.({ properties: cloned });
88
139
  };
140
+ // The one place that edits `suppressedTargets` membership for a single target — every other commit
141
+ // path that touches it (turning suppression on/off, reverting to Inherited, deleting a rule that
142
+ // leaves a stale entry behind) funnels through here instead of hand-rolling the same add/remove
143
+ // filter, so `rulesNext` is the only thing each caller still has to think about for itself.
144
+ const setTargetMode = (target, mode, rulesNext = rules) => {
145
+ const value = targetToValue(target);
146
+ const suppressedTargets = config?.suppressedTargets ?? [];
147
+ const suppressedTargetsNext = mode === "off"
148
+ ? suppressedTargets.some((existing) => targetToValue(existing) === value)
149
+ ? suppressedTargets
150
+ : [...suppressedTargets, target]
151
+ : suppressedTargets.filter((existing) => targetToValue(existing) !== value);
152
+ commit(rulesNext, enabled, suppressedTargetsNext);
153
+ };
154
+ // Turn a still-Inherited target off (Custom-with-zero-rules, per the engine's suppression
155
+ // convention) or back on — never touches `rules`, only `suppressedTargets` membership.
156
+ const setSuppressed = (target, suppressed) => setTargetMode(target, suppressed ? "off" : "inherited");
89
157
  const saveRule = (rule, isNew) => {
90
158
  const rulesNext = isNew
91
159
  ? [...rules, rule]
@@ -95,31 +163,63 @@ export function ConditionalFormattingSection({ properties, propertiesMeta, insig
95
163
  commit(rulesNext, isNew ? true : enabled);
96
164
  setDialog(null);
97
165
  };
98
- const deleteRule = (id) => commit(rules.filter((rule) => rule.id !== id), enabled);
166
+ // Also strips the deleted rule's target from `suppressedTargets`, in case it's stale-listed there
167
+ // too (e.g. externally-authored data, or a target once suppressed then later given its own
168
+ // rule without cleanup) — otherwise the target would stay Custom-with-zero-rules (Off) instead
169
+ // of reverting to Inherited once its last rule is gone, contradicting decision 4's "free
170
+ // consequence" and the "Use default rule" revert flow this also backs.
171
+ const deleteRule = (id) => {
172
+ const rulesNext = rules.filter((rule) => rule.id !== id);
173
+ const target = rules.find((rule) => rule.id === id)?.target;
174
+ if (target) {
175
+ setTargetMode(target, "inherited", rulesNext);
176
+ }
177
+ else {
178
+ commit(rulesNext, enabled);
179
+ }
180
+ };
181
+ // Distinct from `deleteRule`: a target can have several stacked custom rules (the Add flow
182
+ // doesn't prevent authoring more than one against the same target), so reverting it to
183
+ // Inherited must clear ALL of them — deleting just the one open in the dialog would leave the
184
+ // rest still marking the target Custom in `resolvePerTargetConditionalFormatting`. Also clears
185
+ // `ruleId` specifically (regardless of what target it's currently stored under) so a
186
+ // retarget-then-revert doesn't strand the edited rule under its original target.
187
+ const revertToInherited = (target, ruleId) => {
188
+ const value = targetToValue(target);
189
+ const rulesNext = rules.filter((rule) => rule.id !== ruleId && targetToValue(rule.target) !== value);
190
+ setTargetMode(target, "inherited", rulesNext);
191
+ };
99
192
  const chipLabels = {
100
193
  invalid: intl.formatMessage(conditionalFormattingMessages.ruleInvalid),
101
194
  invalidValue: intl.formatMessage(conditionalFormattingMessages.ruleInvalidValue),
102
195
  edit: intl.formatMessage(conditionalFormattingMessages.ruleEdit),
103
196
  delete: intl.formatMessage(conditionalFormattingMessages.ruleDelete),
104
197
  };
105
- const viewLabel = intl.formatMessage(conditionalFormattingMessages.ruleView);
106
- return (_jsxs(ConfigSection, { id: SECTION_ID, className: "gd-cf-section", title: conditionalFormattingMessages.sectionTitle.id, propertiesMeta: propertiesMeta, properties: properties, pushData: pushData, canBeToggled: true, toggledOn: enabled, toggleDisabled: isLoading, onToggle: (checked) => commit(rules, checked), children: [
107
- _jsx("span", { className: "s-cf-popover-anchor gd-cf-section__anchor" }), _jsxs("div", { className: "gd-cf-section__subcategory", children: [
108
- _jsx("span", { className: "gd-cf-section__rules-label", children: intl.formatMessage(conditionalFormattingMessages.rulesLabel) }), _jsx("span", { className: "gd-cf-section__divider" }), _jsx(Button, { className: "gd-button-link gd-cf-section__add", iconLeft: "gd-icon-plus", value: intl.formatMessage(conditionalFormattingMessages.addRule), disabled: !canAddRule, onClick: () => setDialog({ kind: "custom", rule: newRule(targetOptions[0]), isNew: true }) })
109
- ] }), rules.length === 0 ? (
110
- // The empty-state hint would contradict an inherited row rendered just below it.
111
- semanticRows.length === 0 ? (_jsx("div", { className: "gd-cf-section__empty", children: intl.formatMessage(conditionalFormattingMessages.empty) })) : null) : (_jsx("div", { className: "gd-cf-section__rules", children: _jsx(ReorderList, { items: rules, getKey: (rule) => rule.id, onReorder: (next) => commit(next, enabled), renderItem: (rule, slot) => {
112
- const option = findTargetOption(targetOptions, rule.target);
113
- // A missing target flags immediately; value validation waits for date metadata.
114
- const invalid = !option || (targetDataReady && !isRuleComplete(rule, option.date));
115
- return (_jsx(RuleChip, { rule: rule, option: option, invalid: invalid, editable: targetDataReady, labels: chipLabels, slot: slot, onEdit: () => setDialog({ kind: "custom", rule, isNew: false }), onDelete: () => deleteRule(rule.id) }));
116
- } }) })), semanticRows.length > 0 ? (_jsxs("div", { className: "gd-cf-section__semantic", children: [
198
+ const editLabel = intl.formatMessage(conditionalFormattingMessages.ruleEdit);
199
+ const dialogIsNew = activeDialog?.kind === "custom" ? activeDialog.isNew : true;
200
+ // A "semantic" dialog's synthetic id never matches anything in `rules` pass a real callback
201
+ // only when there's an actual rule to delete; the dialog itself (from its own isNew/fixedTarget)
202
+ // decides whether to render a disabled delete affordance for the "nothing authored yet" case,
203
+ // not a no-op we hand it.
204
+ const hasAuthoredRule = activeDialog ? rules.some((rule) => rule.id === activeDialog.rule.id) : false;
205
+ return (_jsxs(ConfigSection, { id: SECTION_ID, className: "gd-cf-section", title: conditionalFormattingMessages.sectionTitle.id, propertiesMeta: propertiesMeta, properties: properties, pushData: pushData, canBeToggled: enableConditionalFormatting, toggledOn: enabled, toggleDisabled: isLoading, onToggle: (checked) => commit(rules, checked), children: [
206
+ _jsx("span", { className: "s-cf-popover-anchor gd-cf-section__anchor" }), enableConditionalFormatting ? (_jsxs(_Fragment, { children: [
207
+ _jsxs("div", { className: "gd-cf-section__subcategory", children: [
208
+ _jsx("span", { className: "gd-cf-section__rules-label", children: intl.formatMessage(conditionalFormattingMessages.rulesLabel) }), _jsx("span", { className: "gd-cf-section__divider" }), _jsx(Button, { className: "gd-button-link gd-cf-section__add", iconLeft: "gd-icon-plus", value: intl.formatMessage(conditionalFormattingMessages.addRule), disabled: !canAddRule, onClick: () => setDialog({ kind: "custom", rule: newRule(targetOptions[0]), isNew: true }) })
209
+ ] }), rules.length === 0 ? (
210
+ // The empty-state hint would contradict an inherited row rendered just below it.
211
+ semanticRows.length === 0 ? (_jsx("div", { className: "gd-cf-section__empty", children: intl.formatMessage(conditionalFormattingMessages.empty) })) : null) : (_jsx("div", { className: "gd-cf-section__rules", children: _jsx(ReorderList, { items: rules, getKey: (rule) => rule.id, onReorder: (next) => commit(next, enabled), renderItem: (rule, slot) => {
212
+ const option = findTargetOption(targetOptions, rule.target);
213
+ // A missing target flags immediately; value validation waits for date metadata.
214
+ const invalid = !option || (targetDataReady && !isRuleComplete(rule, option.date));
215
+ return (_jsx(RuleChip, { rule: rule, option: option, invalid: invalid, editable: targetDataReady, labels: chipLabels, slot: slot, onEdit: () => setDialog({ kind: "custom", rule, isNew: false }), onDelete: () => deleteRule(rule.id) }));
216
+ } }) }))] })) : null, semanticRows.length > 0 ? (_jsxs("div", { className: "gd-cf-section__semantic", children: [
117
217
  _jsxs("div", { className: "gd-cf-section__subcategory", children: [
118
218
  _jsx("span", { className: "gd-cf-section__rules-label", children: intl.formatMessage(conditionalFormattingMessages.semanticLabel) }), _jsx("span", { className: "gd-cf-section__divider" })
119
- ] }), _jsx("div", { className: "gd-cf-section__rules", children: semanticRows.map(({ option, semantic }) => (_jsx(SemanticRuleRow, { option: option, viewLabel: viewLabel, onView: () => setDialog({
219
+ ] }), _jsx("div", { className: "gd-cf-section__rules", children: semanticRows.map(({ option, semantic, off }) => (_jsx(SemanticRuleRow, { option: option, editLabel: editLabel, toggleAriaLabel: intl.formatMessage(conditionalFormattingMessages.semanticToggleAriaLabel, { title: option.title }), off: off, onToggleOff: (nextOff) => setSuppressed(option.target, nextOff), onEdit: () => setDialog({
120
220
  kind: "semantic",
121
221
  rule: semanticRuleFor(option, semantic),
122
222
  option,
123
223
  }) }, option.value))) })
124
- ] })) : null, activeDialog ? (_jsx(ConditionalFormattingDialog, { rule: activeDialog.rule, isNew: activeDialog.kind === "custom" && activeDialog.isNew, readOnly: activeDialog.kind === "semantic", fixedTarget: activeDialog.kind === "semantic", targetOptions: targetOptions, separators: separators, dateFilterOptions: dateFilterOptions, dateSettings: dateSettings, alignTo: ".s-cf-popover-anchor", onSave: (rule) => activeDialog.kind === "custom" && saveRule(rule, activeDialog.isNew), onClose: () => setDialog(null) }, dialogKey(activeDialog))) : null] }));
224
+ ] })) : null, activeDialog ? (_jsx(ConditionalFormattingDialog, { rule: activeDialog.rule, isNew: dialogIsNew, fixedTarget: activeDialog.kind === "semantic", semanticByTarget: semanticMap, targetOptions: targetOptions, separators: separators, dateFilterOptions: dateFilterOptions, dateSettings: dateSettings, alignTo: ".s-cf-popover-anchor", onDelete: hasAuthoredRule ? () => deleteRule(activeDialog.rule.id) : undefined, onRevertToDefault: revertToInherited, onSave: (rule) => saveRule(rule, dialogIsNew), onClose: () => setDialog(null) }, dialogKey(activeDialog))) : null] }));
125
225
  }
@@ -51,13 +51,21 @@ export interface ICfTargetData {
51
51
  dates?: Record<string, ICfDateMeta>;
52
52
  /** Semantic-layer conditional formatting carried on the execution result header, by localId. */
53
53
  semantic?: Record<string, ISemanticConditionalFormatting>;
54
+ /**
55
+ * False while `semantic` still belongs to a superseded execution — distinct from `isLoading`,
56
+ * which can flip back to false before the new data view actually lands (or never, if that
57
+ * execution fails), leaving a gap `isLoading` alone can't see. Set false the instant a new
58
+ * execution starts, true again only once a fresh data view actually rebuilds this object.
59
+ * Undefined (e.g. in tests that construct this directly) is treated as fresh.
60
+ */
61
+ semanticFresh?: boolean;
54
62
  }
55
63
  /**
56
64
  * Extracts {@link ICfTargetData} from the executed data view. Titles must come from here — attribute
57
65
  * titles live only on the result headers, and measures resolve to their final header name; formats
58
66
  * living on metric metadata also resolve only here (insight-level formats win in buildTargetOptions).
59
67
  */
60
- export declare function buildCfTargetData(dataView: DataViewFacade): Required<ICfTargetData>;
68
+ export declare function buildCfTargetData(dataView: DataViewFacade): Required<Omit<ICfTargetData, "semanticFresh">>;
61
69
  export declare function buildTargetOptions(insight: IInsightDefinition, data?: ICfTargetData): ITargetOption[];
62
70
  export declare const findTargetOption: (options: ITargetOption[], target: ConditionalFormattingTarget) => ITargetOption | undefined;
63
71
  /** Date-eligible target: the option carries execution-resolved date metadata. */
@@ -65,17 +73,36 @@ export declare const isDateTarget: (option: ITargetOption | undefined) => boolea
65
73
  export declare const targetLocalId: (target: ConditionalFormattingTarget) => string;
66
74
  /**
67
75
  * Whether `target` is already in Custom mode on this insight config — an insight rule targets it,
68
- * or it's explicitly listed in `customTargets`. Mirrors the `customModeTargets` set built by
76
+ * or it's explicitly listed in `suppressedTargets`. Mirrors the `customModeTargets` set built by
69
77
  * sdk-ui-pivot's `resolvePerTargetConditionalFormatting` (semanticConditionalFormatting.ts); the two
70
78
  * packages don't share this helper, so keep them in sync by hand if either changes. Honoring
71
- * `customTargets` here matters even though this PR never writes it: if some other path already
72
- * marked a target Custom, the semantic block must still hide it as Inherited would be wrong.
79
+ * `suppressedTargets` here matters regardless of source: the Section's own "Turn off" toggle writes
80
+ * it (`setSuppressed`/`setTargetMode`), and it can also arrive from AAC or an older panel version
81
+ * either way, if a target is already marked Custom, the semantic block must still hide it as
82
+ * Inherited would be wrong.
73
83
  */
74
84
  export declare const isCustomTarget: (config: IConditionalFormatting | undefined, target: ConditionalFormattingTarget) => boolean;
75
85
  /**
76
- * Synthesizes a read-only rule for the View dialog from an inherited semantic-layer condition set.
77
- * The id mirrors the engine's own `semantic:<kind>:<localId>` convention (semanticConditionalFormatting.ts)
78
- * for consistency onlyit's never persisted or compared.
86
+ * Whether `target` is explicitly suppressed (Custom-with-zero-rules, per the engine's
87
+ * `resolvePerTargetConditionalFormatting`): listed in `suppressedTargets` with no authored rule.
88
+ * This is the "Turn off" state for an otherwise-Inherited target distinct from an authored-Custom
89
+ * target, which is never in `suppressedTargets` in practice (its own rule already makes it Custom).
90
+ */
91
+ export declare const isSuppressedTarget: (config: IConditionalFormatting | undefined, target: ConditionalFormattingTarget) => boolean;
92
+ /**
93
+ * `suppressedTargets` must be OMITTED entirely when empty, never set to `undefined` — persisting the
94
+ * config as JSON drops an `undefined` value either way, but an in-memory object with an explicit
95
+ * `suppressedTargets: undefined` key is NOT deep-equal to one where the key is simply absent, which
96
+ * creates spurious dirty-state noise in the session before that JSON round-trip ever happens. Shared
97
+ * by every place that builds an `IConditionalFormatting` payload (Section today, a future catalog
98
+ * authoring adapter next) so this normalization can't be forgotten in one of them.
99
+ */
100
+ export declare const withSuppressedTargets: (config: IConditionalFormatting, suppressedTargets: readonly ConditionalFormattingTarget[] | undefined) => IConditionalFormatting;
101
+ /**
102
+ * Synthesizes a rule for the dialog from an inherited semantic-layer condition set, seeding it with
103
+ * a display-only id (mirroring the engine's own `semantic:<kind>:<localId>` convention in
104
+ * semanticConditionalFormatting.ts, for consistency only) — the dialog swaps this for a real uuid()
105
+ * the moment the user customizes and saves, so this synthetic id itself is never persisted.
79
106
  */
80
107
  export declare const semanticRuleFor: (option: ITargetOption, semantic: ISemanticConditionalFormatting) => IConditionalFormattingRule;
81
108
  export declare const operatorsForTarget: (kind: "attribute" | "measure", isDate: boolean) => ConditionalFormattingOperator[];
@@ -1 +1 @@
1
- {"version":3,"file":"conditionalFormattingModel.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/configurationControls/conditionalFormatting/conditionalFormattingModel.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"conditionalFormattingModel.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/configurationControls/conditionalFormatting/conditionalFormattingModel.ts"],"names":[],"mappings":"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"}
@@ -14,6 +14,9 @@ export const targetToValue = (target) => target.kind === "measure"
14
14
  * titles live only on the result headers, and measures resolve to their final header name; formats
15
15
  * living on metric metadata also resolve only here (insight-level formats win in buildTargetOptions).
16
16
  */
17
+ // `semanticFresh` is deliberately excluded: it's a caller-owned freshness marker across MULTIPLE
18
+ // calls to this function over time (see PluggablePivotTableNext's handleLoadingChanged/handleDataView),
19
+ // not something derivable from a single dataView.
17
20
  export function buildCfTargetData(dataView) {
18
21
  const titles = {};
19
22
  const formats = {};
@@ -118,21 +121,49 @@ export const isDateTarget = (option) => option?.date !== undefined;
118
121
  export const targetLocalId = (target) => target.kind === "measure" ? target.measureIdentifier : target.attributeIdentifier;
119
122
  /**
120
123
  * Whether `target` is already in Custom mode on this insight config — an insight rule targets it,
121
- * or it's explicitly listed in `customTargets`. Mirrors the `customModeTargets` set built by
124
+ * or it's explicitly listed in `suppressedTargets`. Mirrors the `customModeTargets` set built by
122
125
  * sdk-ui-pivot's `resolvePerTargetConditionalFormatting` (semanticConditionalFormatting.ts); the two
123
126
  * packages don't share this helper, so keep them in sync by hand if either changes. Honoring
124
- * `customTargets` here matters even though this PR never writes it: if some other path already
125
- * marked a target Custom, the semantic block must still hide it as Inherited would be wrong.
127
+ * `suppressedTargets` here matters regardless of source: the Section's own "Turn off" toggle writes
128
+ * it (`setSuppressed`/`setTargetMode`), and it can also arrive from AAC or an older panel version
129
+ * either way, if a target is already marked Custom, the semantic block must still hide it as
130
+ * Inherited would be wrong.
126
131
  */
127
132
  export const isCustomTarget = (config, target) => {
128
133
  const value = targetToValue(target);
129
134
  return ((config?.rules ?? []).some((rule) => targetToValue(rule.target) === value) ||
130
- (config?.customTargets ?? []).some((customTarget) => targetToValue(customTarget) === value));
135
+ (config?.suppressedTargets ?? []).some((suppressedTarget) => targetToValue(suppressedTarget) === value));
131
136
  };
132
137
  /**
133
- * Synthesizes a read-only rule for the View dialog from an inherited semantic-layer condition set.
134
- * The id mirrors the engine's own `semantic:<kind>:<localId>` convention (semanticConditionalFormatting.ts)
135
- * for consistency onlyit's never persisted or compared.
138
+ * Whether `target` is explicitly suppressed (Custom-with-zero-rules, per the engine's
139
+ * `resolvePerTargetConditionalFormatting`): listed in `suppressedTargets` with no authored rule.
140
+ * This is the "Turn off" state for an otherwise-Inherited target distinct from an authored-Custom
141
+ * target, which is never in `suppressedTargets` in practice (its own rule already makes it Custom).
142
+ */
143
+ export const isSuppressedTarget = (config, target) => {
144
+ const value = targetToValue(target);
145
+ return (config?.suppressedTargets ?? []).some((suppressedTarget) => targetToValue(suppressedTarget) === value);
146
+ };
147
+ /**
148
+ * `suppressedTargets` must be OMITTED entirely when empty, never set to `undefined` — persisting the
149
+ * config as JSON drops an `undefined` value either way, but an in-memory object with an explicit
150
+ * `suppressedTargets: undefined` key is NOT deep-equal to one where the key is simply absent, which
151
+ * creates spurious dirty-state noise in the session before that JSON round-trip ever happens. Shared
152
+ * by every place that builds an `IConditionalFormatting` payload (Section today, a future catalog
153
+ * authoring adapter next) so this normalization can't be forgotten in one of them.
154
+ */
155
+ export const withSuppressedTargets = (config, suppressedTargets) => {
156
+ if (suppressedTargets?.length) {
157
+ return { ...config, suppressedTargets };
158
+ }
159
+ const { suppressedTargets: _dropped, ...rest } = config;
160
+ return rest;
161
+ };
162
+ /**
163
+ * Synthesizes a rule for the dialog from an inherited semantic-layer condition set, seeding it with
164
+ * a display-only id (mirroring the engine's own `semantic:<kind>:<localId>` convention in
165
+ * semanticConditionalFormatting.ts, for consistency only) — the dialog swaps this for a real uuid()
166
+ * the moment the user customizes and saves, so this synthetic id itself is never persisted.
136
167
  */
137
168
  export const semanticRuleFor = (option, semantic) => ({
138
169
  id: `semantic:${option.target.kind}:${targetLocalId(option.target)}`,
@@ -1 +1 @@
1
- {"version":3,"file":"PivotTableConfigurationPanel.d.ts","sourceRoot":"","sources":["../../../../src/internal/components/configurationPanels/PivotTableConfigurationPanel.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAKvC,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EACH,KAAK,WAAW,EAInB,MAAM,qBAAqB,CAAC;AAc7B,OAAO,EACH,KAAK,eAAe,EACpB,KAAK,aAAa,EACrB,MAAM,8EAA8E,CAAC;AAQtF,OAAO,EACH,yBAAyB,EACzB,KAAK,+BAA+B,EACvC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,UAAU,kCAAmC,SAAQ,+BAA+B;IAChF,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,6FAA6F;IAC7F,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,eAAe,CAAC;IACjC,UAAU,CAAC,EAAE,WAAW,CAAC;CAC5B;AAED,qBAAa,4BAA6B,SAAQ,yBAAyB,CAAC,kCAAkC,CAAC;IAC3G,UAAmB,iBAAiB,CAAC,WAAW,CAAC,EAAE,WAAW,GAAG,OAAO,CAgBvE;IAED,SAAS,CAAC,wBAAwB,IAAI,SAAS,CAwB9C;IAED,OAAO,CAAC,mBAAmB;IAS3B,OAAO,CAAC,yBAAyB;IAYjC,OAAO,CAAC,sCAAsC;IAU9C,OAAO,CAAC,mBAAmB;IAqD3B,OAAO,CAAC,kCAAkC;IA8B1C,OAAO,CAAC,mBAAmB;CAY9B"}
1
+ {"version":3,"file":"PivotTableConfigurationPanel.d.ts","sourceRoot":"","sources":["../../../../src/internal/components/configurationPanels/PivotTableConfigurationPanel.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAKvC,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EACH,KAAK,WAAW,EAInB,MAAM,qBAAqB,CAAC;AAiB7B,OAAO,EACH,KAAK,eAAe,EACpB,KAAK,aAAa,EACrB,MAAM,8EAA8E,CAAC;AAQtF,OAAO,EACH,yBAAyB,EACzB,KAAK,+BAA+B,EACvC,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,UAAU,kCAAmC,SAAQ,+BAA+B;IAChF,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,6FAA6F;IAC7F,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,eAAe,CAAC;IACjC,UAAU,CAAC,EAAE,WAAW,CAAC;CAC5B;AAED,qBAAa,4BAA6B,SAAQ,yBAAyB,CAAC,kCAAkC,CAAC;IAC3G,UAAmB,iBAAiB,CAAC,WAAW,CAAC,EAAE,WAAW,GAAG,OAAO,CAgBvE;IAED,SAAS,CAAC,wBAAwB,IAAI,SAAS,CA0B9C;IAED,OAAO,CAAC,mBAAmB;IAS3B,OAAO,CAAC,yBAAyB;IAYjC,OAAO,CAAC,sCAAsC;IAU9C,OAAO,CAAC,mBAAmB;IAqD3B,OAAO,CAAC,kCAAkC;IAmC1C,OAAO,CAAC,mBAAmB;CAY9B"}
@@ -6,7 +6,7 @@ import { Bubble, BubbleHoverTrigger } from "@gooddata/sdk-ui-kit";
6
6
  import { messages } from "../../../locales.js";
7
7
  import { BUBBLE_ARROW_OFFSET_X, BUBBLE_ARROW_OFFSET_Y, HIDE_DELAY_DEFAULT, SHOW_DELAY_DEFAULT, } from "../../constants/bubble.js";
8
8
  import { isSetColumnHeadersPositionToLeftAllowed } from "../../utils/controlsHelper.js";
9
- import { isConditionalFormattingEnabled } from "../../utils/propertiesHelper.js";
9
+ import { isConditionalFormattingEnabled, isSemanticConditionalFormattingEnabled, } from "../../utils/propertiesHelper.js";
10
10
  import { CellsControl } from "../configurationControls/CellsControl.js";
11
11
  import { ColumnHeadersPositionControl } from "../configurationControls/ColumnHeadersPositionControl.js";
12
12
  import { ConditionalFormattingSection } from "../configurationControls/conditionalFormatting/ConditionalFormattingSection.js";
@@ -34,8 +34,10 @@ export class PivotTableConfigurationPanel extends ConfigurationPanelContent {
34
34
  const { featureFlags } = this.props;
35
35
  const enableNewPivotTable = featureFlags?.enableNewPivotTable ?? true;
36
36
  const enableConditionalFormatting = isConditionalFormattingEnabled(featureFlags);
37
+ const enableSemanticConditionalFormatting = isSemanticConditionalFormattingEnabled(featureFlags);
37
38
  return (_jsxs(BubbleHoverTrigger, { showDelay: SHOW_DELAY_DEFAULT, hideDelay: HIDE_DELAY_DEFAULT, children: [
38
- _jsxs("div", { children: [this.renderInteractionsSection(), enableNewPivotTable ? this.renderPagingSection() : null, this.renderCanvasSection(), enableNewPivotTable && enableConditionalFormatting
39
+ _jsxs("div", { children: [this.renderInteractionsSection(), enableNewPivotTable ? this.renderPagingSection() : null, this.renderCanvasSection(), enableNewPivotTable &&
40
+ (enableConditionalFormatting || enableSemanticConditionalFormatting)
39
41
  ? this.renderConditionalFormattingSection()
40
42
  : null] }), _jsx(Bubble, { className: this.getBubbleClassNames(), arrowOffsets: { "tc bc": [BUBBLE_ARROW_OFFSET_X, BUBBLE_ARROW_OFFSET_Y] }, alignPoints: [{ align: "tc bc" }], children: _jsx(FormattedMessage, { id: "properties.config.not_applicable" }) })
41
43
  ] }));
@@ -74,8 +76,11 @@ export class PivotTableConfigurationPanel extends ConfigurationPanelContent {
74
76
  return panelConfig.supportsAttributeHierarchies ? (_jsx(ConfigSection, { id: "canvas_section", className: "gd-table-canvas-section", title: messages["canvasTitle"].id, propertiesMeta: propertiesMeta, pushData: pushData, children: canvasSection })) : (canvasSection);
75
77
  }
76
78
  renderConditionalFormattingSection() {
77
- const { properties, propertiesMeta, insight, cfTargetData, cfBackend, cfWorkspace, cfDateSettings, separators, pushData, isLoading, } = this.props;
78
- return (_jsx(ConditionalFormattingSection, { properties: properties, propertiesMeta: propertiesMeta, insight: insight, targetData: cfTargetData, backend: cfBackend, workspace: cfWorkspace, dateSettings: cfDateSettings, separators: separators, pushData: pushData, isLoading: isLoading }));
79
+ const { properties, propertiesMeta, insight, cfTargetData, cfBackend, cfWorkspace, cfDateSettings, separators, pushData, isLoading, featureFlags, } = this.props;
80
+ return (_jsx(ConditionalFormattingSection, { properties: properties, propertiesMeta: propertiesMeta, insight: insight, targetData: cfTargetData, backend: cfBackend, workspace: cfWorkspace, dateSettings: cfDateSettings, separators: separators, pushData: pushData, isLoading: isLoading, enableConditionalFormatting: isConditionalFormattingEnabled(featureFlags),
81
+ // Mirrors `enableSemanticConditionalFormatting` in PluggablePivotTableNext.tsx — its
82
+ // own dedicated flag, independent of insight-level CF authoring.
83
+ enableSemanticConditionalFormatting: isSemanticConditionalFormattingEnabled(featureFlags) }));
79
84
  }
80
85
  renderPagingSection() {
81
86
  const { properties, pushData, isLoading, propertiesMeta } = this.props;
@@ -1 +1 @@
1
- {"version":3,"file":"PluggablePivotTableNext.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/pluggableVisualizations/pivotTableNext/PluggablePivotTableNext.tsx"],"names":[],"mappings":"AAIA,OAAO,EAGH,KAAK,iBAAiB,EACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACH,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,kBAAkB,EACvB,KAAK,SAAS,EAYjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAIH,KAAK,wBAAwB,EAEhC,MAAM,kBAAkB,CAAC;AAM1B,OAAO,EAGH,KAAK,oBAAoB,EAE5B,MAAM,6BAA6B,CAAC;AAKrC,OAAO,EAEH,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,wBAAwB,EAGhC,MAAM,sCAAsC,CAAC;AAoC9C,OAAO,EAAE,8BAA8B,EAAE,MAAM,sCAAsC,CAAC;AAiBtF,wBAAgB,0BAA0B,CACtC,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,wBAAwB,EACrC,QAAQ,EAAE,SAAS,GACpB,oBAAoB,CAoBtB;AAED;;;;;GAKG;AACH,qBAAa,uBAAwB,SAAQ,8BAA8B;IACvE,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAY;IACrC,OAAO,CAAC,mBAAmB,CAAuB;IAElD,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,cAAc,CAAkB;IACxC,OAAO,CAAC,WAAW,CAAuC;IAE1D,OAAO,CAAC,YAAY,CAAqB;IAEzC,YAAY,KAAK,EAAE,aAAa,EAqB/B;IAEM,OAAO,IAAI,IAAI,CAErB;IAEM,yBAAyB,CAC5B,cAAc,EAAE,eAAe,EAC/B,sBAAsB,CAAC,EAAE,eAAe,GACzC,OAAO,CAAC,uBAAuB,CAAC,CA8GlC;IAEe,8BAA8B,CAC1C,mBAAmB,EAAE,QAAQ,EAC7B,gBAAgB,EAAE,iBAAiB,EACnC,0BAA0B,EAAE,OAAO,GACpC,QAAQ,CAWV;IAED,OAAO,CAAC,kBAAkB;IAInB,YAAY,CACf,OAAO,EAAE,SAAS,EAClB,OAAO,EAAE,kBAAkB,EAC3B,gBAAgB,EAAE,iBAAiB,0DActC;IAED,SAAS,CAAC,oBAAoB,CAAC,uBAAuB,EAAE,wBAAwB,GAAG,IAAI,CActF;IAKD,OAAO,CAAC,cAAc,CAIpB;IAIF,OAAO,CAAC,oBAAoB,CAU1B;IAEF,OAAO,CAAC,yBAAyB,CAe/B;IAEF,UAAmB,wBAAwB,CACvC,OAAO,EAAE,SAAS,EAClB,OAAO,EAAE,kBAAkB,EAC3B,qBAAqB,EAAE,GAAG,GAC3B,IAAI,CASN;IAED,UAAmB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAOzE;IAED,SAAS,CAAC,mBAAmB,CACzB,OAAO,EAAE,SAAS,EAClB,OAAO,EAAE,kBAAkB,EAC3B,gBAAgB,EAAE,iBAAiB,GACpC,IAAI,CAsFN;IAED,SAAS,CAAC,wBAAwB,CAAC,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,SAAS,GAAG,IAAI,CA6BxF;IAED,SAAS,CAAC,aAAa,CAAC,OAAO,EAAE,kBAAkB,EAAE,yBAAyB,CAAC,EAAE,GAAG,GAAG,UAAU,EAAE,CAElG;IAED,OAAO,CAAC,wBAAwB;IAYhC,OAAO,CAAC,oBAAoB;IA8B5B,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,cAAc;CAmCzB"}
1
+ {"version":3,"file":"PluggablePivotTableNext.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/pluggableVisualizations/pivotTableNext/PluggablePivotTableNext.tsx"],"names":[],"mappings":"AAIA,OAAO,EAGH,KAAK,iBAAiB,EACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACH,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,kBAAkB,EACvB,KAAK,SAAS,EAYjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAIH,KAAK,wBAAwB,EAEhC,MAAM,kBAAkB,CAAC;AAM1B,OAAO,EAGH,KAAK,oBAAoB,EAE5B,MAAM,6BAA6B,CAAC;AAKrC,OAAO,EAEH,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,wBAAwB,EAGhC,MAAM,sCAAsC,CAAC;AAoC9C,OAAO,EAAE,8BAA8B,EAAE,MAAM,sCAAsC,CAAC;AAiBtF,wBAAgB,0BAA0B,CACtC,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,wBAAwB,EACrC,QAAQ,EAAE,SAAS,GACpB,oBAAoB,CAoBtB;AAED;;;;;GAKG;AACH,qBAAa,uBAAwB,SAAQ,8BAA8B;IACvE,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAY;IACrC,OAAO,CAAC,mBAAmB,CAAuB;IAElD,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,cAAc,CAAkB;IACxC,OAAO,CAAC,WAAW,CAAuC;IAE1D,OAAO,CAAC,YAAY,CAAqB;IAEzC,YAAY,KAAK,EAAE,aAAa,EAqB/B;IAEM,OAAO,IAAI,IAAI,CAErB;IAEM,yBAAyB,CAC5B,cAAc,EAAE,eAAe,EAC/B,sBAAsB,CAAC,EAAE,eAAe,GACzC,OAAO,CAAC,uBAAuB,CAAC,CA8GlC;IAEe,8BAA8B,CAC1C,mBAAmB,EAAE,QAAQ,EAC7B,gBAAgB,EAAE,iBAAiB,EACnC,0BAA0B,EAAE,OAAO,GACpC,QAAQ,CAWV;IAED,OAAO,CAAC,kBAAkB;IAInB,YAAY,CACf,OAAO,EAAE,SAAS,EAClB,OAAO,EAAE,kBAAkB,EAC3B,gBAAgB,EAAE,iBAAiB,0DActC;IAED,SAAS,CAAC,oBAAoB,CAAC,uBAAuB,EAAE,wBAAwB,GAAG,IAAI,CActF;IAKD,OAAO,CAAC,cAAc,CAIpB;IAIF,OAAO,CAAC,oBAAoB,CAe1B;IAEF,OAAO,CAAC,yBAAyB,CAe/B;IAEF,UAAmB,wBAAwB,CACvC,OAAO,EAAE,SAAS,EAClB,OAAO,EAAE,kBAAkB,EAC3B,qBAAqB,EAAE,GAAG,GAC3B,IAAI,CASN;IAED,UAAmB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAOzE;IAED,SAAS,CAAC,mBAAmB,CACzB,OAAO,EAAE,SAAS,EAClB,OAAO,EAAE,kBAAkB,EAC3B,gBAAgB,EAAE,iBAAiB,GACpC,IAAI,CAqFN;IAED,SAAS,CAAC,wBAAwB,CAAC,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,SAAS,GAAG,IAAI,CA6BxF;IAED,SAAS,CAAC,aAAa,CAAC,OAAO,EAAE,kBAAkB,EAAE,yBAAyB,CAAC,EAAE,GAAG,GAAG,UAAU,EAAE,CAElG;IAED,OAAO,CAAC,wBAAwB;IAYhC,OAAO,CAAC,oBAAoB;IA8B5B,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,cAAc;CAmCzB"}
@@ -11,7 +11,7 @@ import { configureOverTimeComparison, configurePercent } from "../../../utils/bu
11
11
  import { getAllItemsByType, getTotalsFromBucket, limitNumberOfMeasuresInBuckets, removeDuplicateBucketItems, sanitizeFilters, } from "../../../utils/bucketHelper.js";
12
12
  import { isSetColumnHeadersPositionToLeftAllowed } from "../../../utils/controlsHelper.js";
13
13
  import { generateDimensions } from "../../../utils/dimensions.js";
14
- import { getColumnHeadersPositionFromProperties, getColumnWidthsFromProperties, getConditionalFormattingFromProperties, getGrandTotalsPositionFromProperties, getMeasureGroupDimensionFromProperties, getPageSizeFromProperties, getPaginationFromProperties, getPivotTableProperties, getReferencePointWithSupportedProperties, getSupportedPropertiesControls, getTextWrappingFromProperties, isConditionalFormattingEnabled, } from "../../../utils/propertiesHelper.js";
14
+ import { getColumnHeadersPositionFromProperties, getColumnWidthsFromProperties, getEffectiveConditionalFormatting, getGrandTotalsPositionFromProperties, getMeasureGroupDimensionFromProperties, getPageSizeFromProperties, getPaginationFromProperties, getPivotTableProperties, getReferencePointWithSupportedProperties, getSupportedPropertiesControls, getTextWrappingFromProperties, isSemanticConditionalFormattingEnabled, } from "../../../utils/propertiesHelper.js";
15
15
  import { getPivotTableNextDefaultUiConfig, getPivotTableNextMeasuresLimit, setPivotTableNextUiConfig, } from "../../../utils/uiConfigHelpers/pivotTableNextUiConfigHelper.js";
16
16
  import { buildCfTargetData, } from "../../configurationControls/conditionalFormatting/conditionalFormattingModel.js";
17
17
  import { PivotTableConfigurationPanel } from "../../configurationPanels/PivotTableConfigurationPanel.js";
@@ -190,19 +190,24 @@ export class PluggablePivotTableNext extends AbstractPluggableVisualization {
190
190
  // it here to surface the resolved titles instead of leaving fallback names until the next render.
191
191
  handleDataView = (dataView) => {
192
192
  this.onDataView(dataView);
193
- this.cfTargetData = buildCfTargetData(dataView);
193
+ this.cfTargetData = { ...buildCfTargetData(dataView), semanticFresh: true };
194
194
  this.renderConfigurationPanel(this.currentInsight, this.currentOptions);
195
195
  };
196
196
  // A new execution (e.g. the insight's columns changed) begins with isLoading=true; drop stale
197
197
  // titles so the config panel never shows a previous insight's names. handleDataView repopulates.
198
198
  handleLoadingChanged = (loadingState) => {
199
199
  if (loadingState.isLoading) {
200
- // Formats and element suggestions are deliberately kept: clearing them would flip an open
201
- // percent rule's input from "40" to "0.4" and blank suggestions mid-edit. Semantic-layer
202
- // rules have no such open-edit session to protect (they're view-only in this panel), and a
203
- // reused localId could otherwise show/open a DIFFERENT target's inherited rule until the
204
- // next data view lands (or indefinitely on a failed execution) clear them alongside titles.
205
- this.cfTargetData = { ...this.cfTargetData, titles: {}, semantic: {} };
200
+ // Formats, element suggestions, and semantic-layer rules are deliberately kept: clearing
201
+ // formats/suggestions would flip an open percent rule's input from "40" to "0.4" and blank
202
+ // suggestions mid-edit; clearing semantic would do worse it can close a held-open semantic
203
+ // dialog outright (resolveActiveDialog treats a missing entry as invalidated), discarding an
204
+ // in-progress edit. A reused localId could in principle show a stale target's inherited rule
205
+ // until the next data view lands, same accepted tradeoff already made for formats/suggestions.
206
+ // `semanticFresh: false` marks that stale rule as such the instant this execution starts —
207
+ // onLoadingChanged(false) can fire before handleDataView actually rebuilds `semantic` (see
208
+ // handleDataView's own comment), or never at all if this execution fails, so `isLoading`
209
+ // alone can't gate anything that must not act on a superseded semantic map.
210
+ this.cfTargetData = { ...this.cfTargetData, titles: {}, semanticFresh: false };
206
211
  }
207
212
  this.onLoadingChanged(loadingState);
208
213
  };
@@ -267,7 +272,7 @@ export class PluggablePivotTableNext extends AbstractPluggableVisualization {
267
272
  const growToFit = this.environment === DASHBOARDS_ENVIRONMENT;
268
273
  const isInEditMode = config?.isInEditMode;
269
274
  const pagination = getPaginationFromProperties(insightProperties(insight));
270
- const enableConditionalFormatting = isConditionalFormattingEnabled(this.settings);
275
+ const enableSemanticConditionalFormatting = isSemanticConditionalFormattingEnabled(this.settings);
271
276
  const tableConfig = {
272
277
  ...createPivotTableNextConfig(config, this.environment, this.settings),
273
278
  ...customVisualizationConfig,
@@ -284,16 +289,11 @@ export class PluggablePivotTableNext extends AbstractPluggableVisualization {
284
289
  grandTotalsPosition: getGrandTotalsPositionFromProperties(insightProperties(insight)),
285
290
  pagination,
286
291
  enableCellSelection: !isInEditMode,
287
- // Gated behind the feature flag: when disabled, never pass any CF config so an insight
288
- // saved with rules (or a runtime InsightView config) renders plain, with no UI to edit it.
289
- // Prefer a runtime config (InsightView config prop) over the insight's saved rules.
290
- conditionalFormatting: enableConditionalFormatting
291
- ? (customVisualizationConfig?.conditionalFormatting ??
292
- getConditionalFormattingFromProperties(insightProperties(insight)))
293
- : undefined,
292
+ conditionalFormatting: getEffectiveConditionalFormatting(insight, this.settings, customVisualizationConfig?.conditionalFormatting),
294
293
  // The engine inherits from descriptors independently of `conditionalFormatting` above
295
- // (even `undefined` still inherits) — it needs its own explicit gate behind the flag.
296
- enableSemanticConditionalFormatting: enableConditionalFormatting,
294
+ // (even `undefined` still inherits) — it needs its own explicit gate behind its own flag,
295
+ // separate from insight-level CF authoring.
296
+ enableSemanticConditionalFormatting,
297
297
  };
298
298
  // Only pass pageSize when pagination is enabled
299
299
  const pageSize = getPageSizeFromProperties(insightProperties(insight));
@@ -1,4 +1,4 @@
1
- import { type IInsightDefinition, type ISettings } from "@gooddata/sdk-model";
1
+ import { type IInsightDefinition } from "@gooddata/sdk-model";
2
2
  import { type PivotTableNextConfig, type PivotTableNextConfigWithConditionalFormatting } from "@gooddata/sdk-ui-pivot/next";
3
3
  import { type IEmbeddingCodeContext } from "../../../interfaces/VisualizationDescriptor.js";
4
4
  import { type PropMeta, type PropWithMeta } from "../../../utils/embeddingCodeGenerator/types.js";
@@ -7,7 +7,6 @@ import { type PropMeta, type PropWithMeta } from "../../../utils/embeddingCodeGe
7
7
  * type; every other snippet stays on the `@public` {@link PivotTableNextConfig}.
8
8
  */
9
9
  export declare function pivotTableNextConfigPropMeta(config: unknown): PropMeta;
10
- export declare function isPivotTableNext(insightDefinition: IInsightDefinition, settings: ISettings | undefined): boolean;
11
10
  export declare function pivotTableNextConfigFromInsight(insight: IInsightDefinition, ctx: IEmbeddingCodeContext | undefined): PivotTableNextConfigWithConditionalFormatting;
12
11
  export declare function pivotTableNextConfigForInsightViewComponent(): PropWithMeta<PivotTableNextConfig>;
13
12
  //# sourceMappingURL=pivotTableNextConfigFromInsight.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pivotTableNextConfigFromInsight.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/pluggableVisualizations/pivotTableNext/pivotTableNextConfigFromInsight.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,KAAK,kBAAkB,EACvB,KAAK,SAAS,EAGjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAEH,KAAK,oBAAoB,EACzB,KAAK,6CAA6C,EACrD,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,gDAAgD,CAAC;AAC5F,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,YAAY,EAAE,MAAM,gDAAgD,CAAC;AA2BlG;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,OAAO,GAAG,QAAQ,CAMtE;AAED,wBAAgB,gBAAgB,CAC5B,iBAAiB,EAAE,kBAAkB,EACrC,QAAQ,EAAE,SAAS,GAAG,SAAS,GAChC,OAAO,CAGT;AAED,wBAAgB,+BAA+B,CAC3C,OAAO,EAAE,kBAAkB,EAC3B,GAAG,EAAE,qBAAqB,GAAG,SAAS,GACvC,6CAA6C,CA0B/C;AAED,wBAAgB,2CAA2C,IAAI,YAAY,CAAC,oBAAoB,CAAC,CAKhG"}
1
+ {"version":3,"file":"pivotTableNextConfigFromInsight.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/pluggableVisualizations/pivotTableNext/pivotTableNextConfigFromInsight.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,kBAAkB,EAAqB,MAAM,qBAAqB,CAAC;AACjF,OAAO,EAEH,KAAK,oBAAoB,EACzB,KAAK,6CAA6C,EACrD,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,gDAAgD,CAAC;AAC5F,OAAO,EAAE,KAAK,QAAQ,EAAE,KAAK,YAAY,EAAE,MAAM,gDAAgD,CAAC;AA0BlG;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,OAAO,GAAG,QAAQ,CAMtE;AAED,wBAAgB,+BAA+B,CAC3C,OAAO,EAAE,kBAAkB,EAC3B,GAAG,EAAE,qBAAqB,GAAG,SAAS,GACvC,6CAA6C,CAwB/C;AAED,wBAAgB,2CAA2C,IAAI,YAAY,CAAC,oBAAoB,CAAC,CAKhG"}