@gooddata/sdk-ui-ext 11.47.0-alpha.2 → 11.47.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 (133) hide show
  1. package/NOTICE +3 -3
  2. package/esm/index.d.ts +1 -1
  3. package/esm/index.d.ts.map +1 -1
  4. package/esm/internal/components/configurationControls/conditionalFormatting/CfSelect.d.ts +14 -0
  5. package/esm/internal/components/configurationControls/conditionalFormatting/CfSelect.d.ts.map +1 -0
  6. package/esm/internal/components/configurationControls/conditionalFormatting/CfSelect.js +25 -0
  7. package/esm/internal/components/configurationControls/conditionalFormatting/ConditionEditor.d.ts +18 -0
  8. package/esm/internal/components/configurationControls/conditionalFormatting/ConditionEditor.d.ts.map +1 -0
  9. package/esm/internal/components/configurationControls/conditionalFormatting/ConditionEditor.js +132 -0
  10. package/esm/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingDialog.d.ts +4 -1
  11. package/esm/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingDialog.d.ts.map +1 -1
  12. package/esm/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingDialog.js +15 -116
  13. package/esm/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingSection.d.ts +5 -3
  14. package/esm/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingSection.d.ts.map +1 -1
  15. package/esm/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingSection.js +10 -10
  16. package/esm/internal/components/configurationControls/conditionalFormatting/ReorderList.d.ts +25 -0
  17. package/esm/internal/components/configurationControls/conditionalFormatting/ReorderList.d.ts.map +1 -0
  18. package/esm/internal/components/configurationControls/conditionalFormatting/ReorderList.js +94 -0
  19. package/esm/internal/components/configurationControls/conditionalFormatting/conditionalFormattingModel.d.ts +66 -7
  20. package/esm/internal/components/configurationControls/conditionalFormatting/conditionalFormattingModel.d.ts.map +1 -1
  21. package/esm/internal/components/configurationControls/conditionalFormatting/conditionalFormattingModel.js +165 -21
  22. package/esm/internal/components/configurationControls/conditionalFormatting/reorderListModel.d.ts +4 -0
  23. package/esm/internal/components/configurationControls/conditionalFormatting/reorderListModel.d.ts.map +1 -0
  24. package/esm/internal/components/configurationControls/conditionalFormatting/reorderListModel.js +16 -0
  25. package/esm/internal/components/configurationPanels/ConfigurationPanelContent.d.ts +0 -1
  26. package/esm/internal/components/configurationPanels/ConfigurationPanelContent.d.ts.map +1 -1
  27. package/esm/internal/components/configurationPanels/PivotTableConfigurationPanel.d.ts +9 -2
  28. package/esm/internal/components/configurationPanels/PivotTableConfigurationPanel.d.ts.map +1 -1
  29. package/esm/internal/components/configurationPanels/PivotTableConfigurationPanel.js +4 -4
  30. package/esm/internal/components/pluggableVisualizations/pivotTableNext/PluggablePivotTableNext.d.ts +1 -1
  31. package/esm/internal/components/pluggableVisualizations/pivotTableNext/PluggablePivotTableNext.d.ts.map +1 -1
  32. package/esm/internal/components/pluggableVisualizations/pivotTableNext/PluggablePivotTableNext.js +8 -7
  33. package/esm/internal/translations/de-DE.localization-bundle.d.ts +12 -0
  34. package/esm/internal/translations/de-DE.localization-bundle.d.ts.map +1 -1
  35. package/esm/internal/translations/de-DE.localization-bundle.js +12 -0
  36. package/esm/internal/translations/en-AU.localization-bundle.d.ts +12 -0
  37. package/esm/internal/translations/en-AU.localization-bundle.d.ts.map +1 -1
  38. package/esm/internal/translations/en-AU.localization-bundle.js +12 -0
  39. package/esm/internal/translations/en-GB.localization-bundle.d.ts +12 -0
  40. package/esm/internal/translations/en-GB.localization-bundle.d.ts.map +1 -1
  41. package/esm/internal/translations/en-GB.localization-bundle.js +12 -0
  42. package/esm/internal/translations/en-US.localization-bundle.d.ts +8 -0
  43. package/esm/internal/translations/en-US.localization-bundle.d.ts.map +1 -1
  44. package/esm/internal/translations/en-US.localization-bundle.js +8 -0
  45. package/esm/internal/translations/es-419.localization-bundle.d.ts +12 -0
  46. package/esm/internal/translations/es-419.localization-bundle.d.ts.map +1 -1
  47. package/esm/internal/translations/es-419.localization-bundle.js +12 -0
  48. package/esm/internal/translations/es-ES.localization-bundle.d.ts +12 -0
  49. package/esm/internal/translations/es-ES.localization-bundle.d.ts.map +1 -1
  50. package/esm/internal/translations/es-ES.localization-bundle.js +12 -0
  51. package/esm/internal/translations/fi-FI.localization-bundle.d.ts +12 -0
  52. package/esm/internal/translations/fi-FI.localization-bundle.d.ts.map +1 -1
  53. package/esm/internal/translations/fi-FI.localization-bundle.js +12 -0
  54. package/esm/internal/translations/fr-CA.localization-bundle.d.ts +12 -0
  55. package/esm/internal/translations/fr-CA.localization-bundle.d.ts.map +1 -1
  56. package/esm/internal/translations/fr-CA.localization-bundle.js +12 -0
  57. package/esm/internal/translations/fr-FR.localization-bundle.d.ts +12 -0
  58. package/esm/internal/translations/fr-FR.localization-bundle.d.ts.map +1 -1
  59. package/esm/internal/translations/fr-FR.localization-bundle.js +12 -0
  60. package/esm/internal/translations/id-ID.localization-bundle.d.ts +12 -0
  61. package/esm/internal/translations/id-ID.localization-bundle.d.ts.map +1 -1
  62. package/esm/internal/translations/id-ID.localization-bundle.js +12 -0
  63. package/esm/internal/translations/it-IT.localization-bundle.d.ts +12 -0
  64. package/esm/internal/translations/it-IT.localization-bundle.d.ts.map +1 -1
  65. package/esm/internal/translations/it-IT.localization-bundle.js +12 -0
  66. package/esm/internal/translations/ja-JP.localization-bundle.d.ts +12 -0
  67. package/esm/internal/translations/ja-JP.localization-bundle.d.ts.map +1 -1
  68. package/esm/internal/translations/ja-JP.localization-bundle.js +12 -0
  69. package/esm/internal/translations/ko-KR.localization-bundle.d.ts +12 -0
  70. package/esm/internal/translations/ko-KR.localization-bundle.d.ts.map +1 -1
  71. package/esm/internal/translations/ko-KR.localization-bundle.js +12 -0
  72. package/esm/internal/translations/nl-NL.localization-bundle.d.ts +12 -0
  73. package/esm/internal/translations/nl-NL.localization-bundle.d.ts.map +1 -1
  74. package/esm/internal/translations/nl-NL.localization-bundle.js +12 -0
  75. package/esm/internal/translations/pl-PL.localization-bundle.d.ts +12 -0
  76. package/esm/internal/translations/pl-PL.localization-bundle.d.ts.map +1 -1
  77. package/esm/internal/translations/pl-PL.localization-bundle.js +12 -0
  78. package/esm/internal/translations/pt-BR.localization-bundle.d.ts +12 -0
  79. package/esm/internal/translations/pt-BR.localization-bundle.d.ts.map +1 -1
  80. package/esm/internal/translations/pt-BR.localization-bundle.js +12 -0
  81. package/esm/internal/translations/pt-PT.localization-bundle.d.ts +12 -0
  82. package/esm/internal/translations/pt-PT.localization-bundle.d.ts.map +1 -1
  83. package/esm/internal/translations/pt-PT.localization-bundle.js +12 -0
  84. package/esm/internal/translations/ru-RU.localization-bundle.d.ts +12 -0
  85. package/esm/internal/translations/ru-RU.localization-bundle.d.ts.map +1 -1
  86. package/esm/internal/translations/ru-RU.localization-bundle.js +12 -0
  87. package/esm/internal/translations/sl-SI.localization-bundle.d.ts +12 -0
  88. package/esm/internal/translations/sl-SI.localization-bundle.d.ts.map +1 -1
  89. package/esm/internal/translations/sl-SI.localization-bundle.js +12 -0
  90. package/esm/internal/translations/th-TH.localization-bundle.d.ts +12 -0
  91. package/esm/internal/translations/th-TH.localization-bundle.d.ts.map +1 -1
  92. package/esm/internal/translations/th-TH.localization-bundle.js +12 -0
  93. package/esm/internal/translations/tr-TR.localization-bundle.d.ts +12 -0
  94. package/esm/internal/translations/tr-TR.localization-bundle.d.ts.map +1 -1
  95. package/esm/internal/translations/tr-TR.localization-bundle.js +12 -0
  96. package/esm/internal/translations/uk-UA.localization-bundle.d.ts +12 -0
  97. package/esm/internal/translations/uk-UA.localization-bundle.d.ts.map +1 -1
  98. package/esm/internal/translations/uk-UA.localization-bundle.js +12 -0
  99. package/esm/internal/translations/vi-VN.localization-bundle.d.ts +12 -0
  100. package/esm/internal/translations/vi-VN.localization-bundle.d.ts.map +1 -1
  101. package/esm/internal/translations/vi-VN.localization-bundle.js +12 -0
  102. package/esm/internal/translations/zh-HK.localization-bundle.d.ts +12 -0
  103. package/esm/internal/translations/zh-HK.localization-bundle.d.ts.map +1 -1
  104. package/esm/internal/translations/zh-HK.localization-bundle.js +12 -0
  105. package/esm/internal/translations/zh-Hans.localization-bundle.d.ts +12 -0
  106. package/esm/internal/translations/zh-Hans.localization-bundle.d.ts.map +1 -1
  107. package/esm/internal/translations/zh-Hans.localization-bundle.js +12 -0
  108. package/esm/internal/translations/zh-Hant.localization-bundle.d.ts +12 -0
  109. package/esm/internal/translations/zh-Hant.localization-bundle.d.ts.map +1 -1
  110. package/esm/internal/translations/zh-Hant.localization-bundle.js +12 -0
  111. package/esm/locales.d.ts +6 -0
  112. package/esm/locales.d.ts.map +1 -1
  113. package/esm/locales.js +2 -0
  114. package/esm/sdk-ui-ext.d.ts +19 -5
  115. package/esm/share/ObjectShareDialog.d.ts.map +1 -1
  116. package/esm/share/ObjectShareDialog.js +2 -2
  117. package/esm/share/objectShareController.helpers.d.ts +20 -6
  118. package/esm/share/objectShareController.helpers.d.ts.map +1 -1
  119. package/esm/share/objectShareController.helpers.js +40 -9
  120. package/esm/share/objectShareController.types.d.ts +15 -4
  121. package/esm/share/objectShareController.types.d.ts.map +1 -1
  122. package/esm/share/useAccessList.d.ts +0 -1
  123. package/esm/share/useAccessList.d.ts.map +1 -1
  124. package/esm/share/useAccessList.js +74 -83
  125. package/esm/share/useObjectShareController.d.ts +3 -1
  126. package/esm/share/useObjectShareController.d.ts.map +1 -1
  127. package/esm/share/useObjectShareController.js +12 -19
  128. package/package.json +21 -21
  129. package/styles/css/main.css +102 -13
  130. package/styles/css/main.css.map +1 -1
  131. package/styles/internal/css/conditional_formatting.css +102 -13
  132. package/styles/internal/css/conditional_formatting.css.map +1 -1
  133. package/styles/internal/scss/conditional_formatting.scss +138 -13
@@ -0,0 +1,94 @@
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ // (C) 2026 GoodData Corporation
3
+ import { Fragment, useRef, useState } from "react";
4
+ import cx from "classnames";
5
+ import { moveItem, resolveTarget, startsInFormField } from "./reorderListModel.js";
6
+ /**
7
+ * Drag-to-reorder for a small vertical list, on native HTML5 DnD (no dependency, no provider).
8
+ * The render prop keeps the consumer's own row markup; the whole row drags (the grip is a hover
9
+ * affordance). Pointer-only — no keyboard reorder yet.
10
+ */
11
+ export function ReorderList({ items, getKey, onReorder, renderItem }) {
12
+ const [dragIndex, setDragIndex] = useState(null);
13
+ // Insert gap (0..n), canonical: one gap = one indicator position.
14
+ const [dropGap, setDropGap] = useState(null);
15
+ // Enter/leave counter across all rows; at 0 the cursor is outside every row → hide the indicator.
16
+ const overCount = useRef(0);
17
+ const enabled = items.length > 1;
18
+ const reset = () => {
19
+ setDragIndex(null);
20
+ setDropGap(null);
21
+ overCount.current = 0;
22
+ };
23
+ const moves = dragIndex !== null && dropGap !== null && resolveTarget(dragIndex, dropGap) !== null;
24
+ return (_jsx(_Fragment, { children: items.map((item, index) => {
25
+ const slot = enabled
26
+ ? {
27
+ handle: (
28
+ // Visual affordance only; the whole row initiates the drag.
29
+ _jsx("span", { className: "gd-cf-reorder__handle", "aria-hidden": "true" })),
30
+ className: cx("gd-cf-reorder-item", {
31
+ "gd-cf-reorder-item--dragging": dragIndex === index,
32
+ "gd-cf-reorder-item--drop-above": moves && dropGap === index,
33
+ "gd-cf-reorder-item--drop-below": moves && index === items.length - 1 && dropGap === items.length,
34
+ }),
35
+ rootProps: {
36
+ draggable: true,
37
+ onPointerDown: (e) => {
38
+ // dragstart fires on the row itself, never on the field under the
39
+ // cursor — so decide at press time whether this gesture may drag.
40
+ e.currentTarget.draggable = !startsInFormField(e.target);
41
+ },
42
+ onDragStart: (e) => {
43
+ // Only drags of selected text target the field itself — cancel those.
44
+ if (startsInFormField(e.target)) {
45
+ e.preventDefault();
46
+ return;
47
+ }
48
+ setDragIndex(index);
49
+ e.dataTransfer.effectAllowed = "move";
50
+ // Firefox starts a drag only when some data is set.
51
+ e.dataTransfer.setData("text/plain", String(index));
52
+ },
53
+ onDragEnter: () => {
54
+ if (dragIndex !== null) {
55
+ overCount.current += 1;
56
+ }
57
+ },
58
+ onDragOver: (e) => {
59
+ // dragIndex is set only by our own dragstart — foreign drags are ignored.
60
+ if (dragIndex === null) {
61
+ return;
62
+ }
63
+ e.preventDefault();
64
+ e.dataTransfer.dropEffect = "move";
65
+ const rect = e.currentTarget.getBoundingClientRect();
66
+ const gap = e.clientY < rect.top + rect.height / 2 ? index : index + 1;
67
+ setDropGap(gap);
68
+ },
69
+ onDragLeave: () => {
70
+ if (dragIndex === null) {
71
+ return;
72
+ }
73
+ overCount.current = Math.max(0, overCount.current - 1);
74
+ if (overCount.current === 0) {
75
+ setDropGap(null);
76
+ }
77
+ },
78
+ onDrop: (e) => {
79
+ e.preventDefault();
80
+ if (dragIndex !== null && dropGap !== null) {
81
+ const to = resolveTarget(dragIndex, dropGap);
82
+ if (to !== null) {
83
+ onReorder(moveItem(items, dragIndex, to));
84
+ }
85
+ }
86
+ reset();
87
+ },
88
+ onDragEnd: reset,
89
+ },
90
+ }
91
+ : { handle: null, rootProps: {}, className: "" };
92
+ return _jsx(Fragment, { children: renderItem(item, slot, index) }, getKey(item));
93
+ }) }));
94
+ }
@@ -1,19 +1,33 @@
1
1
  import { type IInsightDefinition } from "@gooddata/sdk-model";
2
2
  import { type DataViewFacade } from "@gooddata/sdk-ui";
3
- import { type ConditionalFormattingOperator, type ConditionalFormattingTarget, type IConditionalFormattingCondition, type IConditionalFormattingRule } from "@gooddata/sdk-ui-pivot/next";
3
+ import { type ConditionalFormattingOperator, type ConditionalFormattingTarget, type ConditionalFormattingValue, type IConditionalFormattingCondition, type IConditionalFormattingRule } from "@gooddata/sdk-ui-pivot/next";
4
4
  /** A selectable measure/attribute for the rule's "applies to" dropdown. */
5
5
  export interface ITargetOption {
6
+ /** `measure:<localId>` | `attribute:<localId>` */
6
7
  value: string;
7
8
  title: string;
8
9
  target: ConditionalFormattingTarget;
10
+ /** Percent-formatted measure: the value input edits display units (40), the rule stores raw (0.4). */
11
+ isPercent?: boolean;
12
+ /** Element suggestions from the current (paged) result — a convenience over free text, never a constraint. */
13
+ elements?: readonly string[];
14
+ }
15
+ /** Execution-resolved data the CF UI needs per localId; flows as one object from the pluggable down. */
16
+ export interface ICfTargetData {
17
+ /** Data-view titles by localId; preferred over the insight's often-absent titles. Empty until first load. */
18
+ titles?: Record<string, string>;
19
+ /** Data-view formats by localId; a lagging fallback — insight-derivable info wins. */
20
+ formats?: Record<string, string>;
21
+ /** Distinct attribute element values by localId; drives value autocomplete. */
22
+ elements?: Record<string, string[]>;
9
23
  }
10
24
  /**
11
- * Builds a `localIdentifier -> display title` map from the executed data view. Attribute titles live
12
- * only on the result headers (not on the insight/buckets), so this is the only reliable source for
13
- * human attribute names; measures also resolve to their final header name here.
25
+ * Extracts {@link ICfTargetData} from the executed data view. Titles must come from here — attribute
26
+ * titles live only on the result headers, and measures resolve to their final header name; formats
27
+ * living on metric metadata also resolve only here (insight-level formats win in buildTargetOptions).
14
28
  */
15
- export declare function buildTitlesByLocalId(dataView: DataViewFacade): Record<string, string>;
16
- export declare function buildTargetOptions(insight: IInsightDefinition, titles?: Record<string, string>): ITargetOption[];
29
+ export declare function buildCfTargetData(dataView: DataViewFacade): Required<ICfTargetData>;
30
+ export declare function buildTargetOptions(insight: IInsightDefinition, data?: ICfTargetData): ITargetOption[];
17
31
  export declare const findTargetOption: (options: ITargetOption[], target: ConditionalFormattingTarget) => ITargetOption | undefined;
18
32
  export declare const targetLocalId: (target: ConditionalFormattingTarget) => string;
19
33
  export declare const operatorsForKind: (kind: "attribute" | "measure") => ConditionalFormattingOperator[];
@@ -21,8 +35,53 @@ export declare const operatorIcon: (operator: ConditionalFormattingOperator) =>
21
35
  export declare const targetIcon: (kind: "attribute" | "measure") => string;
22
36
  export type OperatorArity = "none" | "single" | "range";
23
37
  export declare const operatorArity: (operator: ConditionalFormattingOperator) => OperatorArity;
38
+ export type ConditionValueEditor = "none" | "number" | "combobox" | "text" | "range";
39
+ /**
40
+ * Which editor a condition's value renders with. Element suggestions apply only to attribute
41
+ * Is / Is not (per design) — substring operators keep the plain text input.
42
+ */
43
+ export declare const valueEditorKind: (condition: IConditionalFormattingCondition, kind: "attribute" | "measure", hasSuggestions: boolean) => ConditionValueEditor;
44
+ /** Fresh empty operand of the right shape for the operator. */
45
+ export declare const emptyValueForOperator: (operator: ConditionalFormattingOperator) => ConditionalFormattingValue;
24
46
  export declare const newCondition: () => IConditionalFormattingCondition;
25
47
  export declare const newRule: (target: ConditionalFormattingTarget) => IConditionalFormattingRule;
26
- /** A rule is saveable when every condition has the operand its operator requires. */
48
+ /**
49
+ * Rule re-pointed at a new target. Crossing target kinds invalidates the operators, so conditions
50
+ * reset; crossing the percent boundary within measures changes the value units out from under the
51
+ * user, so operators stay but values clear (Save disables until re-entered).
52
+ */
53
+ export declare const ruleWithTarget: (rule: IConditionalFormattingRule, next: ITargetOption, previous: ITargetOption | undefined) => IConditionalFormattingRule;
54
+ /**
55
+ * Percent = the format contains a `%` outside quoted/escaped literals (those render a % sign without
56
+ * percent scaling). A heuristic by design: it only picks the authoring input's display units; the
57
+ * contract compares the raw value either way. Scaled formats are out of scope.
58
+ */
59
+ export declare const isPercentFormat: (format: string | undefined) => boolean;
60
+ /** Raw stored number -> the number the user sees in a percent input (×100). */
61
+ export declare const rawToDisplayNumber: (raw: number, percent: boolean) => number;
62
+ /** Number entered in a percent input -> the raw number stored and compared (÷100). */
63
+ export declare const displayToRawNumber: (display: number, percent: boolean) => number;
64
+ export type ConditionalFormattingFieldError = "rangeOrder";
65
+ export interface IConditionErrors {
66
+ range?: ConditionalFormattingFieldError;
67
+ }
68
+ /**
69
+ * A non-numeric measure literal can only come from stored data (the number input cannot produce one)
70
+ * and would render as an invisible value; coerce it to the empty sentinel the UI can actually show —
71
+ * the disabled Save button then covers it like any other missing value.
72
+ */
73
+ export declare const sanitizeRuleForEditing: (rule: IConditionalFormattingRule) => IConditionalFormattingRule;
74
+ export interface IConditionValidation {
75
+ /** A required operand is absent — gates Save, never shown as an error. */
76
+ missing: boolean;
77
+ /** Present-but-invalid input — surfaced inline by the editor (and also gates Save). */
78
+ errors: IConditionErrors;
79
+ }
80
+ /**
81
+ * Single source for both validation surfaces, making "empty is incomplete, not invalid" structural:
82
+ * {@link isRuleComplete} derives from the whole result, the editor renders only `errors`.
83
+ */
84
+ export declare const validateCondition: (condition: IConditionalFormattingCondition, kind: "attribute" | "measure") => IConditionValidation;
85
+ /** A rule is saveable when every condition has a valid operand for its operator. */
27
86
  export declare const isRuleComplete: (rule: IConditionalFormattingRule) => boolean;
28
87
  //# sourceMappingURL=conditionalFormattingModel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"conditionalFormattingModel.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/configurationControls/conditionalFormatting/conditionalFormattingModel.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,KAAK,kBAAkB,EAS1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAe,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EACH,KAAK,6BAA6B,EAClC,KAAK,2BAA2B,EAChC,KAAK,+BAA+B,EACpC,KAAK,0BAA0B,EAClC,MAAM,6BAA6B,CAAC;AAIrC,2EAA2E;AAC3E,MAAM,WAAW,aAAa;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,2BAA2B,CAAC;CACvC;AAOD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CASrF;AAED,wBAAgB,kBAAkB,CAC9B,OAAO,EAAE,kBAAkB,EAG3B,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACpC,aAAa,EAAE,CA0BjB;AAED,eAAO,MAAM,gBAAgB,8FAGmE,CAAC;AAEjG,eAAO,MAAM,aAAa,iDAC2D,CAAC;AAwCtF,eAAO,MAAM,gBAAgB,oEAEuE,CAAC;AAgBrG,eAAO,MAAM,YAAY,iEAGxB,CAAC;AAIF,eAAO,MAAM,UAAU,2CAC0C,CAAC;AAElE,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;AAExD,eAAO,MAAM,aAAa,4DAKzB,CAAC;AAIF,eAAO,MAAM,YAAY,uCAMvB,CAAC;AAEH,eAAO,MAAM,OAAO,qEAIlB,CAAC;AA2BH,qFAAqF;AACrF,eAAO,MAAM,cAAc,+CAE+D,CAAC"}
1
+ {"version":3,"file":"conditionalFormattingModel.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/configurationControls/conditionalFormatting/conditionalFormattingModel.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,KAAK,kBAAkB,EAa1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAe,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EACH,KAAK,6BAA6B,EAClC,KAAK,2BAA2B,EAChC,KAAK,0BAA0B,EAC/B,KAAK,+BAA+B,EACpC,KAAK,0BAA0B,EAClC,MAAM,6BAA6B,CAAC;AAIrC,2EAA2E;AAC3E,MAAM,WAAW,aAAa;IAC1B,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,2BAA2B,CAAC;IACpC,sGAAsG;IACtG,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gHAA8G;IAC9G,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAChC;AAUD,wGAAwG;AACxG,MAAM,WAAW,aAAa;IAC1B,6GAA6G;IAC7G,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,wFAAsF;IACtF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACvC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,cAAc,GAAG,QAAQ,CAAC,aAAa,CAAC,CAcnF;AAiCD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,kBAAkB,EAAE,IAAI,GAAE,aAAkB,GAAG,aAAa,EAAE,CAkCzG;AAED,eAAO,MAAM,gBAAgB,8FAGmE,CAAC;AAEjG,eAAO,MAAM,aAAa,iDAC2D,CAAC;AAwCtF,eAAO,MAAM,gBAAgB,oEAEuE,CAAC;AAgBrG,eAAO,MAAM,YAAY,iEAGxB,CAAC;AAIF,eAAO,MAAM,UAAU,2CAC0C,CAAC;AAElE,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;AAExD,eAAO,MAAM,aAAa,4DAKzB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC;AAKrF;;;GAGG;AACH,eAAO,MAAM,eAAe,8HAgB3B,CAAC;AAIF,+DAA+D;AAC/D,eAAO,MAAM,qBAAqB,yEAcjC,CAAC;AAEF,eAAO,MAAM,YAAY,uCAMvB,CAAC;AAEH,eAAO,MAAM,OAAO,qEAIlB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,cAAc,4HAmB1B,CAAC;AAIF;;;;GAIG;AACH,eAAO,MAAM,eAAe,yCAKN,CAAC;AAKvB,gFAA+E;AAC/E,eAAO,MAAM,kBAAkB,2CACO,CAAC;AAEvC,uFAAsF;AACtF,eAAO,MAAM,kBAAkB,+CACe,CAAC;AAI/C,MAAM,MAAM,+BAA+B,GAAG,YAAY,CAAC;AAE3D,MAAM,WAAW,gBAAgB;IAC7B,KAAK,CAAC,EAAE,+BAA+B,CAAC;CAC3C;AAID;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,kEAclC,CAAC;AAEF,MAAM,WAAW,oBAAoB;IACjC,4EAA0E;IAC1E,OAAO,EAAE,OAAO,CAAC;IACjB,yFAAuF;IACvF,MAAM,EAAE,gBAAgB,CAAC;CAC5B;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB,qGA0B7B,CAAC;AAEF,oFAAoF;AACpF,eAAO,MAAM,cAAc,+CAKrB,CAAC"}
@@ -1,37 +1,78 @@
1
1
  // (C) 2026 GoodData Corporation
2
2
  import { v4 as uuid } from "uuid";
3
- import { attributeAlias, attributeLocalId, bucketAttributes, insightBucket, insightMeasures, measureAlias, measureLocalId, measureTitle, } from "@gooddata/sdk-model";
3
+ import { attributeAlias, attributeLocalId, bucketAttributes, insightBucket, insightMeasures, isResultAttributeHeader, measureAlias, measureDoesComputeRatio, measureFormat, measureLocalId, measureTitle, resultHeaderName, } from "@gooddata/sdk-model";
4
4
  import { BucketNames } from "@gooddata/sdk-ui";
5
5
  import { CF_DEFAULT_COLOR } from "./conditionalFormattingColors.js";
6
+ // Cap suggestions so a high-cardinality attribute can't build a huge suggestion list.
7
+ const MAX_ELEMENT_SUGGESTIONS = 200;
6
8
  const targetToValue = (target) => target.kind === "measure"
7
9
  ? `measure:${target.measureIdentifier}`
8
10
  : `attribute:${target.attributeIdentifier}`;
9
11
  /**
10
- * Builds a `localIdentifier -> display title` map from the executed data view. Attribute titles live
11
- * only on the result headers (not on the insight/buckets), so this is the only reliable source for
12
- * human attribute names; measures also resolve to their final header name here.
12
+ * Extracts {@link ICfTargetData} from the executed data view. Titles must come from here — attribute
13
+ * titles live only on the result headers, and measures resolve to their final header name; formats
14
+ * living on metric metadata also resolve only here (insight-level formats win in buildTargetOptions).
13
15
  */
14
- export function buildTitlesByLocalId(dataView) {
16
+ export function buildCfTargetData(dataView) {
15
17
  const titles = {};
18
+ const formats = {};
16
19
  for (const measure of dataView.meta().measureDescriptors()) {
17
- titles[measure.measureHeaderItem.localIdentifier] = measure.measureHeaderItem.name;
20
+ const { localIdentifier, name, format } = measure.measureHeaderItem;
21
+ titles[localIdentifier] = name;
22
+ if (format) {
23
+ formats[localIdentifier] = format;
24
+ }
18
25
  }
19
26
  for (const attribute of dataView.meta().attributeDescriptors()) {
20
27
  titles[attribute.attributeHeader.localIdentifier] = attribute.attributeHeader.formOf.name;
21
28
  }
22
- return titles;
29
+ return { titles, formats, elements: buildElementsByLocalId(dataView) };
23
30
  }
24
- export function buildTargetOptions(insight,
25
- // Resolved data-view titles keyed by localIdentifier; preferred over the insight's own (often
26
- // absent) titles/aliases. Empty until the first data view arrives.
27
- titles = {}) {
31
+ // Attribute header groups align by index with the dimension's attribute descriptors; the collected
32
+ // elements are only as complete as the loaded (paged) result.
33
+ function buildElementsByLocalId(dataView) {
34
+ const elements = {};
35
+ const meta = dataView.meta();
36
+ meta.dimensions().forEach((_dimension, dimIndex) => {
37
+ const descriptors = meta.attributeDescriptorsForDim(dimIndex);
38
+ const headerGroups = meta.attributeHeadersForDim(dimIndex);
39
+ descriptors.forEach((descriptor, index) => {
40
+ const localId = descriptor.attributeHeader.localIdentifier;
41
+ const seen = new Set();
42
+ for (const header of headerGroups[index] ?? []) {
43
+ if (seen.size >= MAX_ELEMENT_SUGGESTIONS) {
44
+ break;
45
+ }
46
+ // attributeHeadersForDim's type is optimistic — it filters groups by their FIRST
47
+ // header only, so with (sub)totals on, total headers ("sum") ride along in the group.
48
+ if (!isResultAttributeHeader(header)) {
49
+ continue;
50
+ }
51
+ const name = resultHeaderName(header);
52
+ if (name) {
53
+ seen.add(name);
54
+ }
55
+ }
56
+ elements[localId] = [...seen];
57
+ });
58
+ });
59
+ return elements;
60
+ }
61
+ export function buildTargetOptions(insight, data = {}) {
62
+ const { titles = {}, formats = {}, elements = {} } = data;
28
63
  const measures = insightMeasures(insight).map((measure) => {
29
64
  const localId = measureLocalId(measure);
30
65
  const target = { kind: "measure", measureIdentifier: localId };
66
+ const insightFormat = measureFormat(measure);
31
67
  return {
32
68
  value: targetToValue(target),
33
69
  title: titles[localId] ?? measureTitle(measure) ?? measureAlias(measure) ?? localId,
34
70
  target,
71
+ // fillMissingFormat's precedence: an explicit insight format (reacts to edits instantly,
72
+ // never stale), then show-in-% (percent by definition), then the execution fallback.
73
+ isPercent: insightFormat
74
+ ? isPercentFormat(insightFormat)
75
+ : measureDoesComputeRatio(measure) || isPercentFormat(formats[localId]),
35
76
  };
36
77
  });
37
78
  // Only ROW attributes are offerable: a rule formats a row/cell by the row's value, and a row
@@ -47,6 +88,7 @@ titles = {}) {
47
88
  value: targetToValue(target),
48
89
  title: titles[localId] ?? attributeAlias(attribute) ?? localId,
49
90
  target,
91
+ elements: elements[localId],
50
92
  };
51
93
  });
52
94
  return [...measures, ...attributes];
@@ -113,7 +155,39 @@ export const operatorArity = (operator) => {
113
155
  }
114
156
  return RANGE_OPERATORS.has(operator) ? "range" : "single";
115
157
  };
158
+ const isEqualityOperator = (operator) => operator === "EQUAL_TO" || operator === "NOT_EQUAL_TO";
159
+ /**
160
+ * Which editor a condition's value renders with. Element suggestions apply only to attribute
161
+ * Is / Is not (per design) — substring operators keep the plain text input.
162
+ */
163
+ export const valueEditorKind = (condition, kind, hasSuggestions) => {
164
+ switch (operatorArity(condition.operator)) {
165
+ case "none":
166
+ return "none";
167
+ case "range":
168
+ return "range";
169
+ case "single":
170
+ if (kind === "measure") {
171
+ return "number";
172
+ }
173
+ return hasSuggestions && isEqualityOperator(condition.operator) ? "combobox" : "text";
174
+ }
175
+ };
116
176
  // --- Factories -------------------------------------------------------------------------------
177
+ /** Fresh empty operand of the right shape for the operator. */
178
+ export const emptyValueForOperator = (operator) => {
179
+ switch (operatorArity(operator)) {
180
+ case "none":
181
+ return { kind: "none" };
182
+ case "range":
183
+ // NaN = a bound not yet entered (rendered blank). isRuleComplete blocks Save until both
184
+ // bounds are finite, so NaN stays dialog-local and never persists (it would
185
+ // JSON-serialize to null); the contract type can stay a tight `{ from: number; to: number }`.
186
+ return { kind: "literalRange", from: NaN, to: NaN };
187
+ case "single":
188
+ return { kind: "literal", value: "" };
189
+ }
190
+ };
117
191
  export const newCondition = () => ({
118
192
  id: uuid(),
119
193
  operator: "ALL",
@@ -126,24 +200,94 @@ export const newRule = (target) => ({
126
200
  target,
127
201
  conditions: [newCondition()],
128
202
  });
129
- // --- Validation ------------------------------------------------------------------------------
130
- const isConditionComplete = (condition, kind) => {
203
+ /**
204
+ * Rule re-pointed at a new target. Crossing target kinds invalidates the operators, so conditions
205
+ * reset; crossing the percent boundary within measures changes the value units out from under the
206
+ * user, so operators stay but values clear (Save disables until re-entered).
207
+ */
208
+ export const ruleWithTarget = (rule, next, previous) => {
209
+ if (next.target.kind !== rule.target.kind) {
210
+ return { ...rule, target: next.target, conditions: [newCondition()] };
211
+ }
212
+ if ((next.isPercent ?? false) !== (previous?.isPercent ?? false)) {
213
+ return {
214
+ ...rule,
215
+ target: next.target,
216
+ conditions: rule.conditions.map((condition) => ({
217
+ ...condition,
218
+ value: emptyValueForOperator(condition.operator),
219
+ })),
220
+ };
221
+ }
222
+ return { ...rule, target: next.target };
223
+ };
224
+ // --- Percent-aware value input ---------------------------------------------------------------
225
+ /**
226
+ * Percent = the format contains a `%` outside quoted/escaped literals (those render a % sign without
227
+ * percent scaling). A heuristic by design: it only picks the authoring input's display units; the
228
+ * contract compares the raw value either way. Scaled formats are out of scope.
229
+ */
230
+ export const isPercentFormat = (format) => typeof format === "string" &&
231
+ format
232
+ .replace(/"[^"]*"/g, "")
233
+ .replace(/\\./g, "")
234
+ .includes("%");
235
+ // 0.4 * 100 = 40.00000000000001 in IEEE-754; toPrecision(12) drops the tail.
236
+ const denoise = (n) => Number(n.toPrecision(12));
237
+ /** Raw stored number -> the number the user sees in a percent input (×100). */
238
+ export const rawToDisplayNumber = (raw, percent) => percent ? denoise(raw * 100) : raw;
239
+ /** Number entered in a percent input -> the raw number stored and compared (÷100). */
240
+ export const displayToRawNumber = (display, percent) => percent ? denoise(display / 100) : display;
241
+ const isBlank = (value) => String(value).trim() === "";
242
+ /**
243
+ * A non-numeric measure literal can only come from stored data (the number input cannot produce one)
244
+ * and would render as an invisible value; coerce it to the empty sentinel the UI can actually show —
245
+ * the disabled Save button then covers it like any other missing value.
246
+ */
247
+ export const sanitizeRuleForEditing = (rule) => {
248
+ if (rule.target.kind !== "measure") {
249
+ return rule;
250
+ }
251
+ return {
252
+ ...rule,
253
+ conditions: rule.conditions.map((condition) => condition.value.kind === "literal" &&
254
+ !isBlank(condition.value.value) &&
255
+ Number.isNaN(Number(condition.value.value))
256
+ ? { ...condition, value: { kind: "literal", value: "" } }
257
+ : condition),
258
+ };
259
+ };
260
+ /**
261
+ * Single source for both validation surfaces, making "empty is incomplete, not invalid" structural:
262
+ * {@link isRuleComplete} derives from the whole result, the editor renders only `errors`.
263
+ */
264
+ export const validateCondition = (condition, kind) => {
131
265
  const { value } = condition;
132
266
  switch (operatorArity(condition.operator)) {
133
267
  case "none":
134
- return true;
135
- case "range":
136
- return value.kind === "literalRange" && Number.isFinite(value.from) && Number.isFinite(value.to);
268
+ return { missing: false, errors: {} };
269
+ case "range": {
270
+ if (value.kind !== "literalRange") {
271
+ return { missing: true, errors: {} };
272
+ }
273
+ const missing = !Number.isFinite(value.from) || !Number.isFinite(value.to);
274
+ return { missing, errors: !missing && value.from > value.to ? { range: "rangeOrder" } : {} };
275
+ }
137
276
  case "single":
138
277
  if (value.kind !== "literal") {
139
- return false;
278
+ return { missing: true, errors: {} };
140
279
  }
141
280
  // Guard the empty string first: `Number("") === 0` (not NaN), which would otherwise let an
142
281
  // empty measure threshold pass as a valid 0.
143
- return (String(value.value).trim() !== "" &&
144
- (kind === "attribute" || !Number.isNaN(Number(value.value))));
282
+ return {
283
+ missing: isBlank(value.value) || (kind === "measure" && Number.isNaN(Number(value.value))),
284
+ errors: {},
285
+ };
145
286
  }
146
287
  };
147
- /** A rule is saveable when every condition has the operand its operator requires. */
288
+ /** A rule is saveable when every condition has a valid operand for its operator. */
148
289
  export const isRuleComplete = (rule) => rule.conditions.length > 0 &&
149
- rule.conditions.every((condition) => isConditionComplete(condition, rule.target.kind));
290
+ rule.conditions.every((condition) => {
291
+ const { missing, errors } = validateCondition(condition, rule.target.kind);
292
+ return !missing && Object.keys(errors).length === 0;
293
+ });
@@ -0,0 +1,4 @@
1
+ export declare const resolveTarget: (from: number, gap: number) => number | null;
2
+ export declare const moveItem: <T>(items: readonly T[], from: number, to: number) => T[];
3
+ export declare const startsInFormField: (target: EventTarget | null) => boolean;
4
+ //# sourceMappingURL=reorderListModel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reorderListModel.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/configurationControls/conditionalFormatting/reorderListModel.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,aAAa,8CAGzB,CAAC;AAEF,eAAO,MAAM,QAAQ,GAAI,CAAC,uDAKzB,CAAC;AAIF,eAAO,MAAM,iBAAiB,yCACqD,CAAC"}
@@ -0,0 +1,16 @@
1
+ // (C) 2026 GoodData Corporation
2
+ // Splice destination for inserting into gap (0..n), compensating for the dragged row's removal;
3
+ // null = no-op.
4
+ export const resolveTarget = (from, gap) => {
5
+ const to = gap > from ? gap - 1 : gap;
6
+ return to === from ? null : to;
7
+ };
8
+ export const moveItem = (items, from, to) => {
9
+ const next = [...items];
10
+ const [moved] = next.splice(from, 1);
11
+ next.splice(to, 0, moved);
12
+ return next;
13
+ };
14
+ // With the whole row draggable, a press-and-move inside a text field would drag the row instead of
15
+ // selecting text — so drags originating in form fields are cancelled.
16
+ export const startsInFormField = (target) => target instanceof Element && target.closest("input, textarea, select") !== null;
@@ -15,7 +15,6 @@ export interface IConfigurationPanelContentProps<PanelConfig = any> {
15
15
  isError?: boolean;
16
16
  isLoading?: boolean;
17
17
  insight?: IInsightDefinition;
18
- titlesByLocalId?: Record<string, string>;
19
18
  featureFlags?: ISettings;
20
19
  permissions?: IWorkspacePermissions;
21
20
  axis?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"ConfigurationPanelContent.d.ts","sourceRoot":"","sources":["../../../../src/internal/components/configurationPanels/ConfigurationPanelContent.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,EACH,KAAK,kBAAkB,EACvB,KAAK,SAAS,EACd,KAAK,qBAAqB,EAG7B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,SAAS,EAAiB,MAAM,kBAAkB,CAAC;AAEjE,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EACH,KAAK,4BAA4B,EACjC,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,wBAAwB,EAChC,MAAM,mCAAmC,CAAC;AAoB3C,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,WAAW,+BAA+B,CAAC,WAAW,GAAG,GAAG;IAC9D,UAAU,CAAC,EAAE,wBAAwB,CAAC;IACtC,UAAU,CAAC,EAAE,WAAW,CAAC;IACzB,cAAc,CAAC,EAAE,eAAe,CAAC;IACjC,cAAc,CAAC,EAAE,GAAG,CAAC;IACrB,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,WAAW,CAAC,EAAE,qBAAqB,CAAC;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI,CAAC;IAC3B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,2BAA2B,CAAC,EAAE,4BAA4B,CAAC;IAC3D;;;;;;OAMG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACnC;AAED,8BAAsB,yBAAyB,CAC3C,CAAC,SAAS,+BAA+B,GAAG,+BAA+B,CAC7E,SAAQ,aAAa,CAAC,CAAC,CAAC;IACtB,OAAc,YAAY,EAAE,+BAA+B,CAgBzD;IAEF,SAAS,CAAC,uBAAuB,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAExD,YAAY,KAAK,EAAE,CAAC,EAGnB;IAEe,MAAM,4CAQrB;IAED,SAAS,CAAC,QAAQ,CAAC,wBAAwB,IAAI,SAAS,CAAC;IAEzD;;;OAGG;IACH,SAAS,CAAC,iBAAiB,CAAC,YAAY,CAAC,EAAE,WAAW,GAAG,OAAO,CAG/D;IAED,SAAS,CAAC,kBAAkB,IAAI,SAAS,CAsBxC;IAED,SAAS,CAAC,mBAAmB,IAAI,SAAS,CAYzC;IAED,SAAS,CAAC,yBAAyB,IAAI,SAAS,CAwC/C;IAED,SAAS,CAAC,qBAAqB,IAAI,SAAS,CAiB3C;IAED,SAAS,CAAC,sBAAsB,IAAI,SAAS,CAqC5C;IAED,SAAS,CAAC,0BAA0B,IAAI,SAAS,CAehD;IAED,SAAS,CAAC,qBAAqB,IAAI,SAAS,CAU3C;CACJ"}
1
+ {"version":3,"file":"ConfigurationPanelContent.d.ts","sourceRoot":"","sources":["../../../../src/internal/components/configurationPanels/ConfigurationPanelContent.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEtD,OAAO,EACH,KAAK,kBAAkB,EACvB,KAAK,SAAS,EACd,KAAK,qBAAqB,EAG7B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,SAAS,EAAiB,MAAM,kBAAkB,CAAC;AAEjE,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EACH,KAAK,4BAA4B,EACjC,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,wBAAwB,EAChC,MAAM,mCAAmC,CAAC;AAoB3C,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,MAAM,WAAW,+BAA+B,CAAC,WAAW,GAAG,GAAG;IAC9D,UAAU,CAAC,EAAE,wBAAwB,CAAC;IACtC,UAAU,CAAC,EAAE,WAAW,CAAC;IACzB,cAAc,CAAC,EAAE,eAAe,CAAC;IACjC,cAAc,CAAC,EAAE,GAAG,CAAC;IACrB,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,WAAW,CAAC,EAAE,qBAAqB,CAAC;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI,CAAC;IAC3B,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,2BAA2B,CAAC,EAAE,4BAA4B,CAAC;IAC3D;;;;;;OAMG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACnC;AAED,8BAAsB,yBAAyB,CAC3C,CAAC,SAAS,+BAA+B,GAAG,+BAA+B,CAC7E,SAAQ,aAAa,CAAC,CAAC,CAAC;IACtB,OAAc,YAAY,EAAE,+BAA+B,CAgBzD;IAEF,SAAS,CAAC,uBAAuB,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAExD,YAAY,KAAK,EAAE,CAAC,EAGnB;IAEe,MAAM,4CAQrB;IAED,SAAS,CAAC,QAAQ,CAAC,wBAAwB,IAAI,SAAS,CAAC;IAEzD;;;OAGG;IACH,SAAS,CAAC,iBAAiB,CAAC,YAAY,CAAC,EAAE,WAAW,GAAG,OAAO,CAG/D;IAED,SAAS,CAAC,kBAAkB,IAAI,SAAS,CAsBxC;IAED,SAAS,CAAC,mBAAmB,IAAI,SAAS,CAYzC;IAED,SAAS,CAAC,yBAAyB,IAAI,SAAS,CAwC/C;IAED,SAAS,CAAC,qBAAqB,IAAI,SAAS,CAiB3C;IAED,SAAS,CAAC,sBAAsB,IAAI,SAAS,CAqC5C;IAED,SAAS,CAAC,0BAA0B,IAAI,SAAS,CAehD;IAED,SAAS,CAAC,qBAAqB,IAAI,SAAS,CAU3C;CACJ"}
@@ -1,7 +1,13 @@
1
1
  import { type ReactNode } from "react";
2
- import { ConfigurationPanelContent } from "./ConfigurationPanelContent.js";
2
+ import { type ISeparators } from "@gooddata/sdk-model";
3
+ import { type ICfTargetData } from "../configurationControls/conditionalFormatting/conditionalFormattingModel.js";
4
+ import { ConfigurationPanelContent, type IConfigurationPanelContentProps } from "./ConfigurationPanelContent.js";
3
5
  import { type SectionName } from "./sectionName.js";
4
- export declare class PivotTableConfigurationPanel extends ConfigurationPanelContent {
6
+ interface IPivotTableConfigurationPanelProps extends IConfigurationPanelContentProps {
7
+ cfTargetData?: ICfTargetData;
8
+ separators?: ISeparators;
9
+ }
10
+ export declare class PivotTableConfigurationPanel extends ConfigurationPanelContent<IPivotTableConfigurationPanelProps> {
5
11
  protected isControlDisabled(sectionName?: SectionName): boolean;
6
12
  protected renderConfigurationPanel(): ReactNode;
7
13
  private getBubbleClassNames;
@@ -11,4 +17,5 @@ export declare class PivotTableConfigurationPanel extends ConfigurationPanelCont
11
17
  private renderConditionalFormattingSection;
12
18
  private renderPagingSection;
13
19
  }
20
+ export {};
14
21
  //# sourceMappingURL=PivotTableConfigurationPanel.d.ts.map
@@ -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;AAyBvC,OAAO,EAAE,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAC3E,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,qBAAa,4BAA6B,SAAQ,yBAAyB;IACvE,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;IAe1C,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,EACH,KAAK,WAAW,EAInB,MAAM,qBAAqB,CAAC;AAa7B,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,8EAA8E,CAAC;AAQlH,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,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;IAiB1C,OAAO,CAAC,mBAAmB;CAY9B"}
@@ -1,7 +1,7 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import cx from "classnames";
3
3
  import { FormattedMessage } from "react-intl";
4
- import { insightHasAttributes, insightHasMeasures, insightMeasures } from "@gooddata/sdk-model";
4
+ import { insightHasAttributes, insightHasMeasures, insightMeasures, } from "@gooddata/sdk-model";
5
5
  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";
@@ -14,7 +14,7 @@ import { ConfigSection } from "../configurationControls/ConfigSection.js";
14
14
  import { GrandTotalsControl } from "../configurationControls/GrandTotalsControl.js";
15
15
  import { MetricsPositionControl } from "../configurationControls/MetricsPositionControl.js";
16
16
  import { PagingSection } from "../configurationControls/PagingSection.js";
17
- import { ConfigurationPanelContent } from "./ConfigurationPanelContent.js";
17
+ import { ConfigurationPanelContent, } from "./ConfigurationPanelContent.js";
18
18
  export class PivotTableConfigurationPanel extends ConfigurationPanelContent {
19
19
  isControlDisabled(sectionName) {
20
20
  if (sectionName === "interactions.scheduled_exports" ||
@@ -73,8 +73,8 @@ export class PivotTableConfigurationPanel extends ConfigurationPanelContent {
73
73
  return panelConfig.supportsAttributeHierarchies ? (_jsx(ConfigSection, { id: "canvas_section", className: "gd-table-canvas-section", title: messages["canvasTitle"].id, propertiesMeta: propertiesMeta, pushData: pushData, children: canvasSection })) : (canvasSection);
74
74
  }
75
75
  renderConditionalFormattingSection() {
76
- const { properties, propertiesMeta, insight, titlesByLocalId, pushData, isLoading } = this.props;
77
- return (_jsx(ConditionalFormattingSection, { properties: properties, propertiesMeta: propertiesMeta, insight: insight, titlesByLocalId: titlesByLocalId, pushData: pushData, isLoading: isLoading }));
76
+ const { properties, propertiesMeta, insight, cfTargetData, separators, pushData, isLoading } = this.props;
77
+ return (_jsx(ConditionalFormattingSection, { properties: properties, propertiesMeta: propertiesMeta, insight: insight, targetData: cfTargetData, separators: separators, pushData: pushData, isLoading: isLoading }));
78
78
  }
79
79
  renderPagingSection() {
80
80
  const { properties, pushData, isLoading, propertiesMeta } = this.props;
@@ -17,7 +17,7 @@ export declare class PluggablePivotTableNext extends AbstractPluggableVisualizat
17
17
  private readonly settings;
18
18
  private backendCapabilities;
19
19
  private environment;
20
- private titlesByLocalId;
20
+ private cfTargetData;
21
21
  constructor(props: IVisConstruct);
22
22
  unmount(): void;
23
23
  getExtendedReferencePoint(referencePoint: IReferencePoint, previousReferencePoint?: IReferencePoint): Promise<IExtendedReferencePoint>;
@@ -1 +1 @@
1
- {"version":3,"file":"PluggablePivotTableNext.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/pluggableVisualizations/pivotTableNext/PluggablePivotTableNext.tsx"],"names":[],"mappings":"AAIA,OAAO,EAA6B,KAAK,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9F,OAAO,EACH,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,kBAAkB,EACvB,KAAK,SAAS,EAWjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAIH,KAAK,wBAAwB,EAEhC,MAAM,kBAAkB,CAAC;AAM1B,OAAO,EAIH,KAAK,oBAAoB,EAC5B,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;AA+B9C,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;IAClD,OAAO,CAAC,WAAW,CAAuC;IAG1D,OAAO,CAAC,eAAe,CAA8B;IAErD,YAAY,KAAK,EAAE,aAAa,EAa/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,CAK1B;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,CAmFN;IAED,SAAS,CAAC,wBAAwB,CAAC,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,SAAS,GAAG,IAAI,CAyBxF;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,EAA6B,KAAK,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9F,OAAO,EACH,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,kBAAkB,EACvB,KAAK,SAAS,EAWjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAIH,KAAK,wBAAwB,EAEhC,MAAM,kBAAkB,CAAC;AAM1B,OAAO,EAIH,KAAK,oBAAoB,EAC5B,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;AAkC9C,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;IAClD,OAAO,CAAC,WAAW,CAAuC;IAE1D,OAAO,CAAC,YAAY,CAAqB;IAEzC,YAAY,KAAK,EAAE,aAAa,EAa/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,CAO1B;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,CAmFN;IAED,SAAS,CAAC,wBAAwB,CAAC,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,SAAS,GAAG,IAAI,CA0BxF;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"}
@@ -13,7 +13,7 @@ import { isSetColumnHeadersPositionToLeftAllowed } from "../../../utils/controls
13
13
  import { generateDimensions } from "../../../utils/dimensions.js";
14
14
  import { getColumnHeadersPositionFromProperties, getColumnWidthsFromProperties, getConditionalFormattingFromProperties, getGrandTotalsPositionFromProperties, getMeasureGroupDimensionFromProperties, getPageSizeFromProperties, getPaginationFromProperties, getPivotTableProperties, getReferencePointWithSupportedProperties, getSupportedPropertiesControls, getTextWrappingFromProperties, } from "../../../utils/propertiesHelper.js";
15
15
  import { getPivotTableNextDefaultUiConfig, getPivotTableNextMeasuresLimit, setPivotTableNextUiConfig, } from "../../../utils/uiConfigHelpers/pivotTableNextUiConfigHelper.js";
16
- import { buildTitlesByLocalId } from "../../configurationControls/conditionalFormatting/conditionalFormattingModel.js";
16
+ import { buildCfTargetData, } from "../../configurationControls/conditionalFormatting/conditionalFormattingModel.js";
17
17
  import { PivotTableConfigurationPanel } from "../../configurationPanels/PivotTableConfigurationPanel.js";
18
18
  import { AbstractPluggableVisualization } from "../AbstractPluggableVisualization.js";
19
19
  import { addIntersectionFiltersToInsight, modifyBucketsAttributesForDrillDown, sanitizeTableProperties, } from "../drillDownUtil.js";
@@ -53,9 +53,8 @@ export class PluggablePivotTableNext extends AbstractPluggableVisualization {
53
53
  settings;
54
54
  backendCapabilities;
55
55
  environment;
56
- // Resolved from the data view (attribute titles aren't on the insight). Populated on data view;
57
- // the config panel re-renders on load completion and reads it.
58
- titlesByLocalId = {};
56
+ // Populated on data view; the config panel re-renders on load completion and reads it.
57
+ cfTargetData = {};
59
58
  constructor(props) {
60
59
  super(props);
61
60
  this.environment = props.environment;
@@ -179,14 +178,16 @@ export class PluggablePivotTableNext extends AbstractPluggableVisualization {
179
178
  // it here to surface the resolved titles instead of leaving fallback names until the next render.
180
179
  handleDataView = (dataView) => {
181
180
  this.onDataView(dataView);
182
- this.titlesByLocalId = buildTitlesByLocalId(dataView);
181
+ this.cfTargetData = buildCfTargetData(dataView);
183
182
  this.renderConfigurationPanel(this.currentInsight, this.currentOptions);
184
183
  };
185
184
  // A new execution (e.g. the insight's columns changed) begins with isLoading=true; drop stale
186
185
  // titles so the config panel never shows a previous insight's names. handleDataView repopulates.
187
186
  handleLoadingChanged = (loadingState) => {
188
187
  if (loadingState.isLoading) {
189
- this.titlesByLocalId = {};
188
+ // Formats and element suggestions are deliberately kept: clearing them would flip an open
189
+ // percent rule's input from "40" to "0.4" and blank suggestions mid-edit.
190
+ this.cfTargetData = { ...this.cfTargetData, titles: {} };
190
191
  }
191
192
  this.onLoadingChanged(loadingState);
192
193
  };
@@ -301,7 +302,7 @@ export class PluggablePivotTableNext extends AbstractPluggableVisualization {
301
302
  const panelConfig = {
302
303
  supportsAttributeHierarchies: this.backendCapabilities.supportsAttributeHierarchies,
303
304
  };
304
- this.renderFun(_jsx(PivotTableConfigurationPanel, { locale: this.locale, properties: this.visualizationProperties, propertiesMeta: this.propertiesMeta, insight: insight, titlesByLocalId: this.titlesByLocalId, pushData: this.handlePushData, isError: this.getIsError(), isLoading: this.isLoading, featureFlags: this.settings, panelConfig: panelConfig, configurationPanelRenderers: options.custom?.configurationPanelRenderers }), configPanelElement);
305
+ this.renderFun(_jsx(PivotTableConfigurationPanel, { locale: this.locale, properties: this.visualizationProperties, propertiesMeta: this.propertiesMeta, insight: insight, cfTargetData: this.cfTargetData, separators: options.config?.separators, pushData: this.handlePushData, isError: this.getIsError(), isLoading: this.isLoading, featureFlags: this.settings, panelConfig: panelConfig, configurationPanelRenderers: options.custom?.configurationPanelRenderers }), configPanelElement);
305
306
  }
306
307
  }
307
308
  getDimensions(insight, customVisualizationConfig) {