@gooddata/sdk-ui-ext 11.52.0 → 11.53.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +59 -9
- package/esm/index.d.ts +3 -0
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +2 -0
- package/esm/insightView/InsightRenderer.d.ts +6 -3
- package/esm/insightView/InsightRenderer.d.ts.map +1 -1
- package/esm/insightView/InsightRenderer.js +142 -165
- package/esm/internal/components/configurationControls/conditionalFormatting/ConditionEditor.d.ts +3 -1
- package/esm/internal/components/configurationControls/conditionalFormatting/ConditionEditor.d.ts.map +1 -1
- package/esm/internal/components/configurationControls/conditionalFormatting/ConditionEditor.js +20 -6
- package/esm/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingDialog.d.ts +11 -1
- package/esm/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingDialog.d.ts.map +1 -1
- package/esm/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingDialog.js +25 -10
- package/esm/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingSection.d.ts.map +1 -1
- package/esm/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingSection.js +64 -5
- package/esm/internal/components/configurationControls/conditionalFormatting/ReorderList.d.ts +3 -1
- package/esm/internal/components/configurationControls/conditionalFormatting/ReorderList.d.ts.map +1 -1
- package/esm/internal/components/configurationControls/conditionalFormatting/ReorderList.js +2 -2
- package/esm/internal/components/configurationControls/conditionalFormatting/conditionalFormattingModel.d.ts +46 -8
- package/esm/internal/components/configurationControls/conditionalFormatting/conditionalFormattingModel.d.ts.map +1 -1
- package/esm/internal/components/configurationControls/conditionalFormatting/conditionalFormattingModel.js +94 -16
- package/esm/internal/components/configurationControls/conditionalFormatting/useCfDateFilterOptions.d.ts +2 -0
- package/esm/internal/components/configurationControls/conditionalFormatting/useCfDateFilterOptions.d.ts.map +1 -1
- package/esm/internal/components/configurationControls/conditionalFormatting/useCfDateFilterOptions.js +2 -0
- package/esm/internal/components/pluggableVisualizations/pivotTableNext/PluggablePivotTableNext.d.ts.map +1 -1
- package/esm/internal/components/pluggableVisualizations/pivotTableNext/PluggablePivotTableNext.js +8 -2
- package/esm/internal/translations/en-US.localization-bundle.d.ts +12 -0
- package/esm/internal/translations/en-US.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/en-US.localization-bundle.js +12 -0
- package/esm/locales.d.ts +9 -0
- package/esm/locales.d.ts.map +1 -1
- package/esm/locales.js +3 -0
- package/esm/sdk-ui-ext.d.ts +86 -0
- package/package.json +21 -21
- package/styles/css/main.css +59 -0
- package/styles/css/main.css.map +1 -1
- package/styles/internal/css/conditional_formatting.css +59 -0
- package/styles/internal/css/conditional_formatting.css.map +1 -1
- package/styles/internal/scss/conditional_formatting.scss +78 -0
package/esm/internal/components/configurationControls/conditionalFormatting/ConditionEditor.js
CHANGED
|
@@ -9,7 +9,7 @@ import { ColorDropdown } from "../colors/colorDropdown/ColorDropdown.js";
|
|
|
9
9
|
import { CfDateValueDropdown } from "./CfDateValueDropdown.js";
|
|
10
10
|
import { CfSelect } from "./CfSelect.js";
|
|
11
11
|
import { CF_COLOR_PALETTE, colorToHex, hexToColor } from "./conditionalFormattingColors.js";
|
|
12
|
-
import { displayToRawNumber, literalRaw, literalText, operatorIcon, operatorsForTarget, rangeRaw, rawToDisplayNumber, validateCondition, valueEditorKind, valueForOperator, } from "./conditionalFormattingModel.js";
|
|
12
|
+
import { displayToRawNumber, literalRaw, literalText, operatorIcon, operatorsForTarget, rangeRaw, rawToDisplayNumber, rawValueText, validateCondition, valueEditorKind, valueForOperator, valueMatchesEditorKind, } from "./conditionalFormattingModel.js";
|
|
13
13
|
// InputPure's own contract for the same error state.
|
|
14
14
|
const inputErrorProps = ({ hasError, errorId, }) => hasError ? { hasError, accessibilityConfig: { ariaDescribedBy: errorId, ariaInvalid: true } } : {};
|
|
15
15
|
function CfMeasureValueInput({ raw, isPercent, separators, placeholder, onChangeRaw, ...field }) {
|
|
@@ -58,7 +58,7 @@ function CfFieldError({ id, error }) {
|
|
|
58
58
|
message: intl.formatMessage(FIELD_ERROR_MESSAGE[error]),
|
|
59
59
|
}) }));
|
|
60
60
|
}
|
|
61
|
-
function ConditionValueInput({ condition, editorKind, kind, isPercent, separators, suggestions, date, dateFilterOptions, dateSettings, onChange, }) {
|
|
61
|
+
function ConditionValueInput({ condition, editorKind, kind, isPercent, separators, suggestions, date, dateFilterOptions, dateSettings, readOnly, onChange, }) {
|
|
62
62
|
const intl = useIntl();
|
|
63
63
|
const errorId = useId();
|
|
64
64
|
// Empty is only an error once the field has been visited, so authoring a rule top-down never reddens
|
|
@@ -77,6 +77,12 @@ function ConditionValueInput({ condition, editorKind, kind, isPercent, separator
|
|
|
77
77
|
return { hasError, errorId: hasError ? errorId : undefined, onVisit: markVisited(field) };
|
|
78
78
|
};
|
|
79
79
|
const error = invalid ?? emptyIn("value") ?? emptyIn("from") ?? emptyIn("to");
|
|
80
|
+
// A read-only rule may pair a value shape its editor can't represent (e.g. a semantic-layer
|
|
81
|
+
// condition the pivot editor never authors) — fall back to raw text instead of a dead-ended
|
|
82
|
+
// control. Placed after every hook call above so it never changes the hooks called per render.
|
|
83
|
+
if (readOnly && !valueMatchesEditorKind(editorKind, condition.value)) {
|
|
84
|
+
return _jsx("span", { className: "gd-cf-condition__raw-value", children: rawValueText(condition.value) });
|
|
85
|
+
}
|
|
80
86
|
const renderControl = () => {
|
|
81
87
|
switch (editorKind) {
|
|
82
88
|
case "none":
|
|
@@ -126,11 +132,19 @@ function FormatEditor({ format, onChange }) {
|
|
|
126
132
|
] })
|
|
127
133
|
] })) }));
|
|
128
134
|
}
|
|
129
|
-
export function ConditionEditor({ condition, kind, isPercent, separators, suggestions, date, dateFilterOptions, dateSettings, removable, slot, onChange, onRemove, }) {
|
|
135
|
+
export function ConditionEditor({ condition, kind, isPercent, separators, suggestions, date, dateFilterOptions, dateSettings, removable, slot, readOnly = false, onChange, onRemove, }) {
|
|
130
136
|
const intl = useIntl();
|
|
131
137
|
const isDate = date !== undefined;
|
|
138
|
+
const curatedOperators = operatorsForTarget(kind, isDate);
|
|
139
|
+
// A read-only rule may carry an operator the editor doesn't curate for this target (e.g. a
|
|
140
|
+
// semantic-layer rule the pivot editor can't author); keep it selectable so its real label shows
|
|
141
|
+
// instead of leaving the picker blank. Gated on readOnly: sanitizeRuleForEditing normalizes the
|
|
142
|
+
// operator on the editable path, so this must never offer an uncurated choice a user could pick.
|
|
143
|
+
const operators = readOnly && !curatedOperators.includes(condition.operator)
|
|
144
|
+
? [...curatedOperators, condition.operator]
|
|
145
|
+
: curatedOperators;
|
|
132
146
|
// Date operators render label-only (per design), so no icon lookup on that path.
|
|
133
|
-
const operatorItems =
|
|
147
|
+
const operatorItems = operators.map((operator) => ({
|
|
134
148
|
value: operator,
|
|
135
149
|
title: intl.formatMessage((isDate ? conditionalFormattingDateOperatorMessages[operator] : undefined) ??
|
|
136
150
|
conditionalFormattingOperatorMessages[operator]),
|
|
@@ -143,13 +157,13 @@ export function ConditionEditor({ condition, kind, isPercent, separators, sugges
|
|
|
143
157
|
...(condition.format.backgroundColor ? { backgroundColor: condition.format.backgroundColor } : {}),
|
|
144
158
|
};
|
|
145
159
|
return (_jsxs("div", { className: cx("gd-cf-condition", slot.className), ...slot.rootProps, children: [slot.handle, _jsxs("div", { className: "gd-cf-condition__header", children: [
|
|
146
|
-
_jsx("span", { className: "gd-cf-condition__title", children: intl.formatMessage(conditionalFormattingMessages.dialogCondition) }), removable ? (_jsx(Button, { className: "gd-button-link gd-button-icon-only gd-icon-cross", accessibilityConfig: {
|
|
160
|
+
_jsx("span", { className: "gd-cf-condition__title", children: intl.formatMessage(conditionalFormattingMessages.dialogCondition) }), removable ? (_jsx(Button, { className: "gd-button-link gd-button-icon-only gd-icon-cross gd-cf-condition__delete", accessibilityConfig: {
|
|
147
161
|
ariaLabel: intl.formatMessage(conditionalFormattingMessages.dialogRemoveCondition),
|
|
148
162
|
}, onClick: onRemove })) : null] }), _jsx(CfSelect, { value: condition.operator, items: operatorItems, onSelect: (operator) => onChange({
|
|
149
163
|
...condition,
|
|
150
164
|
operator,
|
|
151
165
|
value: valueForOperator(operator, condition.value, isDate),
|
|
152
|
-
}) }), _jsx(ConditionValueInput, { condition: condition, editorKind: editorKind, kind: kind, isPercent: isPercent, separators: separators, suggestions: suggestions, date: date, dateFilterOptions: dateFilterOptions, dateSettings: dateSettings, onChange: onChange }, editorKind), _jsxs("div", { className: "gd-cf-condition__format-row", children: [
|
|
166
|
+
}) }), _jsx(ConditionValueInput, { condition: condition, editorKind: editorKind, kind: kind, isPercent: isPercent, separators: separators, suggestions: suggestions, date: date, dateFilterOptions: dateFilterOptions, dateSettings: dateSettings, readOnly: readOnly, onChange: onChange }, editorKind), _jsxs("div", { className: "gd-cf-condition__format-row", children: [
|
|
153
167
|
_jsx("span", { className: "gd-cf-dialog__label", children: intl.formatMessage(conditionalFormattingMessages.dialogFormat) }), _jsx("div", { className: "gd-cf-preview", children: condition.format.scope === "row" ? (ROW_PREVIEW_SAMPLE.map((sample, index) => (_jsx("span", { className: "gd-cf-preview__cell", style: previewStyle, children: sample }, index)))) : (_jsx("span", { className: "gd-cf-preview__cell", style: previewStyle, children: PREVIEW_SAMPLE[kind] })) }), _jsx(FormatEditor, { format: condition.format, onChange: setFormat })
|
|
154
168
|
] })
|
|
155
169
|
] }));
|
|
@@ -2,6 +2,9 @@ import { type ISeparators } from "@gooddata/sdk-model";
|
|
|
2
2
|
import { type IDateFilterOptionsByType } from "@gooddata/sdk-ui-filters";
|
|
3
3
|
import { type IConditionalFormattingRule } from "@gooddata/sdk-ui-pivot/next";
|
|
4
4
|
import { type ICfDateSettings, type ITargetOption } from "./conditionalFormattingModel.js";
|
|
5
|
+
/**
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
5
8
|
export interface IConditionalFormattingDialogProps {
|
|
6
9
|
rule: IConditionalFormattingRule;
|
|
7
10
|
isNew: boolean;
|
|
@@ -14,8 +17,15 @@ export interface IConditionalFormattingDialogProps {
|
|
|
14
17
|
dateSettings?: ICfDateSettings;
|
|
15
18
|
/** CSS selector of a small, stable element the popover anchors to. */
|
|
16
19
|
alignTo: string;
|
|
20
|
+
/** Renders the dialog non-interactively: no target picker, no editing, no Save button. */
|
|
21
|
+
readOnly?: boolean;
|
|
22
|
+
/** Renders a static "icon + title" header for the rule's target instead of the picker dropdown. */
|
|
23
|
+
fixedTarget?: boolean;
|
|
17
24
|
onSave: (rule: IConditionalFormattingRule) => void;
|
|
18
25
|
onClose: () => void;
|
|
19
26
|
}
|
|
20
|
-
|
|
27
|
+
/**
|
|
28
|
+
* @internal
|
|
29
|
+
*/
|
|
30
|
+
export declare function ConditionalFormattingDialog({ rule: initialRule, isNew, targetOptions, separators, dateFilterOptions, dateSettings, alignTo, readOnly, fixedTarget, onSave, onClose }: IConditionalFormattingDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
21
31
|
//# sourceMappingURL=ConditionalFormattingDialog.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConditionalFormattingDialog.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingDialog.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ConditionalFormattingDialog.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingDialog.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAEzE,OAAO,EAEH,KAAK,0BAA0B,EAClC,MAAM,6BAA6B,CAAC;AAKrC,OAAO,EACH,KAAK,eAAe,EACpB,KAAK,aAAa,EAQrB,MAAM,iCAAiC,CAAC;AAWzC;;GAEG;AACH,MAAM,WAAW,iCAAiC;IAC9C,IAAI,EAAE,0BAA0B,CAAC;IACjC,KAAK,EAAE,OAAO,CAAC;IACf,aAAa,EAAE,aAAa,EAAE,CAAC;IAC/B,uFAAuF;IACvF,UAAU,CAAC,EAAE,WAAW,CAAC;IACzB,mGAAmG;IACnG,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;IAC7C,uFAAuF;IACvF,YAAY,CAAC,EAAE,eAAe,CAAC;IAC/B,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAC;IAChB,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,mGAAmG;IACnG,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,CAAC,IAAI,EAAE,0BAA0B,KAAK,IAAI,CAAC;IACnD,OAAO,EAAE,MAAM,IAAI,CAAC;CACvB;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,EACxC,IAAI,EAAE,WAAW,EACjB,KAAK,EACL,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,YAAY,EACZ,OAAO,EACP,QAAgB,EAChB,WAAmB,EACnB,MAAM,EACN,OAAO,EACV,EAAE,iCAAiC,2CAmKnC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// (C) 2026 GoodData Corporation
|
|
3
3
|
import { useState } from "react";
|
|
4
|
+
import cx from "classnames";
|
|
4
5
|
import { isEqual } from "lodash-es";
|
|
5
6
|
import { useIntl } from "react-intl";
|
|
6
7
|
import { Button, Overlay } from "@gooddata/sdk-ui-kit";
|
|
@@ -15,9 +16,14 @@ const POPOVER_ALIGN_POINTS = [
|
|
|
15
16
|
{ align: "cl cr", offset: { x: -5, y: 0 } },
|
|
16
17
|
{ align: "cr cl", offset: { x: 5, y: 0 } },
|
|
17
18
|
];
|
|
18
|
-
|
|
19
|
+
/**
|
|
20
|
+
* @internal
|
|
21
|
+
*/
|
|
22
|
+
export function ConditionalFormattingDialog({ rule: initialRule, isNew, targetOptions, separators, dateFilterOptions, dateSettings, alignTo, readOnly = false, fixedTarget = false, onSave, onClose, }) {
|
|
19
23
|
const intl = useIntl();
|
|
20
|
-
const initial =
|
|
24
|
+
const initial = readOnly
|
|
25
|
+
? initialRule
|
|
26
|
+
: sanitizeRuleForEditing(initialRule, isDateTarget(findTargetOption(targetOptions, initialRule.target)));
|
|
21
27
|
const [rule, setRule] = useState(initial);
|
|
22
28
|
const updateCondition = (id, next) => setRule((current) => ({
|
|
23
29
|
...current,
|
|
@@ -43,24 +49,33 @@ export function ConditionalFormattingDialog({ rule: initialRule, isNew, targetOp
|
|
|
43
49
|
setRule((current) => ruleWithTarget(current, option, findTargetOption(targetOptions, current.target)));
|
|
44
50
|
};
|
|
45
51
|
const selectedTarget = findTargetOption(targetOptions, rule.target);
|
|
52
|
+
const singleTarget = fixedTarget ? selectedTarget : undefined;
|
|
46
53
|
const isPercent = selectedTarget?.isPercent ?? false;
|
|
47
54
|
const suggestions = selectedTarget?.elements ?? [];
|
|
48
55
|
const complete = isRuleComplete(rule, selectedTarget?.date);
|
|
49
56
|
const isDirty = isNew || !isEqual(rule, initial);
|
|
50
|
-
const title = intl.formatMessage(
|
|
57
|
+
const title = intl.formatMessage(readOnly
|
|
58
|
+
? conditionalFormattingMessages.dialogViewTitle
|
|
59
|
+
: isNew
|
|
60
|
+
? conditionalFormattingMessages.dialogAddTitle
|
|
61
|
+
: conditionalFormattingMessages.dialogEditTitle);
|
|
51
62
|
return (_jsx(Overlay, { alignTo: alignTo, alignPoints: POPOVER_ALIGN_POINTS, closeOnParentScroll: true, closeOnMouseDrag: true, onClose: onClose, children: _jsxs("div", { className: "gd-cf-dialog", "aria-label": title, children: [
|
|
52
63
|
_jsxs("div", { className: "gd-cf-dialog__header", children: [
|
|
53
64
|
_jsx("h3", { className: "gd-cf-dialog__title", children: title }), _jsx(Button, { className: "gd-button-link gd-button-icon-only gd-icon-cross", accessibilityConfig: {
|
|
54
65
|
ariaLabel: intl.formatMessage(conditionalFormattingMessages.dialogCancel),
|
|
55
66
|
}, onClick: onClose })
|
|
56
67
|
] }), _jsxs("div", { className: "gd-cf-dialog__body", children: [
|
|
57
|
-
_jsx("span", { className: "gd-cf-dialog__label", children: intl.formatMessage(conditionalFormattingMessages.dialogTarget) }),
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
_jsx("span", { className: "gd-cf-dialog__label", children: intl.formatMessage(conditionalFormattingMessages.dialogTarget) }), singleTarget ? (_jsxs("div", { className: "gd-cf-dialog__static-target", children: [
|
|
69
|
+
_jsx("span", { className: cx("gd-cf-type-icon", targetIcon(singleTarget.target.kind, isDateTarget(singleTarget))), "aria-hidden": "true" }), _jsx("span", { className: "gd-cf-dialog__static-target-title", children: singleTarget.title })
|
|
70
|
+
] })) : (_jsx("fieldset", { className: "gd-cf-dialog__target-picker", disabled: readOnly, children: _jsx(CfSelect, { value: selectedTarget?.value, items: targetOptions.map((option) => ({
|
|
71
|
+
value: option.value,
|
|
72
|
+
title: option.title,
|
|
73
|
+
icon: targetIcon(option.target.kind, isDateTarget(option)),
|
|
74
|
+
})), onSelect: changeTarget, placeholder: intl.formatMessage(conditionalFormattingMessages.dialogSelectTarget) }) })), _jsxs("fieldset", { disabled: readOnly, className: cx("gd-cf-dialog__editable", {
|
|
75
|
+
"gd-cf-dialog__editable--readonly": readOnly,
|
|
76
|
+
}), children: [_jsx(ReorderList, { items: rule.conditions, getKey: (condition) => condition.id, onReorder: reorderConditions, disabled: readOnly, renderItem: (condition, slot) => (_jsx(ConditionEditor, { condition: condition, kind: rule.target.kind, isPercent: isPercent, separators: separators, suggestions: suggestions, date: selectedTarget?.date, dateFilterOptions: dateFilterOptions, dateSettings: dateSettings, removable: rule.conditions.length > 1, slot: slot, readOnly: readOnly, onChange: (next) => updateCondition(condition.id, next), onRemove: () => removeCondition(condition.id) })) }), _jsx(Button, { className: "gd-button-secondary gd-cf-dialog__add-condition", iconLeft: "gd-icon-plus", value: intl.formatMessage(conditionalFormattingMessages.dialogAddCondition), onClick: addCondition })
|
|
77
|
+
] })
|
|
62
78
|
] }), _jsxs("div", { className: "gd-cf-dialog__footer", children: [
|
|
63
|
-
_jsx(Button, { className: "gd-button-secondary", value: intl.formatMessage(conditionalFormattingMessages.dialogCancel), onClick: onClose }), _jsx(Button, { className: "gd-button-action", value: intl.formatMessage(conditionalFormattingMessages.dialogSave), disabled: !complete || !isDirty, onClick: () => onSave(rule) })
|
|
64
|
-
] })
|
|
79
|
+
_jsx(Button, { className: "gd-button-secondary", value: intl.formatMessage(conditionalFormattingMessages.dialogCancel), onClick: onClose }), readOnly ? null : (_jsx(Button, { className: "gd-button-action", value: intl.formatMessage(conditionalFormattingMessages.dialogSave), disabled: !complete || !isDirty, onClick: () => onSave(rule) }))] })
|
|
65
80
|
] }) }));
|
|
66
81
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConditionalFormattingSection.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingSection.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,
|
|
1
|
+
{"version":3,"file":"ConditionalFormattingSection.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingSection.tsx"],"names":[],"mappings":"AAQA,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EACH,KAAK,kBAAkB,EAEvB,KAAK,WAAW,EACnB,MAAM,qBAAqB,CAAC;AAK7B,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,sCAAsC,CAAC;AAIrF,OAAO,EACH,KAAK,eAAe,EACpB,KAAK,aAAa,EAWrB,MAAM,iCAAiC,CAAC;AAYzC,MAAM,WAAW,kCAAkC;IAC/C,UAAU,CAAC,EAAE,wBAAwB,CAAC;IACtC,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,UAAU,CAAC,EAAE,aAAa,CAAC;IAC3B,yFAAyF;IACzF,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,eAAe,CAAC;IAC/B,UAAU,CAAC,EAAE,WAAW,CAAC;IACzB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;CACtC;AA8GD,wBAAgB,4BAA4B,CAAC,EACzC,UAAU,EACV,cAAc,EACd,OAAO,EACP,UAAU,EACV,OAAO,EACP,SAAS,EACT,YAAY,EACZ,UAAU,EACV,SAAS,EACT,QAAQ,EACX,EAAE,kCAAkC,2CA+KpC"}
|
|
@@ -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, isDateTarget, isRuleComplete, newRule, targetIcon, targetLocalId, } from "./conditionalFormattingModel.js";
|
|
11
|
+
import { buildTargetOptions, findTargetOption, isCustomTarget, isDateTarget, isRuleComplete, newRule, semanticRuleFor, targetIcon, targetLocalId, } from "./conditionalFormattingModel.js";
|
|
12
12
|
import { ReorderList } from "./ReorderList.js";
|
|
13
13
|
import { useCfDateFilterOptions } from "./useCfDateFilterOptions.js";
|
|
14
14
|
const SECTION_ID = "conditionalFormatting_section";
|
|
@@ -18,6 +18,38 @@ function RuleChip({ rule, option, invalid, editable, labels, slot, onEdit, onDel
|
|
|
18
18
|
_jsx("span", { className: `gd-cf-type-icon ${targetIcon(rule.target.kind, isDateTarget(option))}`, "aria-hidden": "true" }), _jsx("span", { className: "gd-cf-rule__title", children: option ? option.title : targetLocalId(rule.target) }), invalid ? (_jsx("span", { className: "gd-cf-rule__invalid", children: option ? labels.invalidValue : labels.invalid })) : null] }), _jsx("span", { className: "gd-cf-rule__delete", children: _jsx(UiIconButton, { icon: "trash", size: "small", variant: "tertiary", isDesctructive: true, label: labels.delete, onClick: onDelete }) })
|
|
19
19
|
] }));
|
|
20
20
|
}
|
|
21
|
+
// A held-open semantic dialog is a snapshot from when "View" was clicked — re-derive it against the
|
|
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) {
|
|
26
|
+
if (dialog?.kind !== "semantic") {
|
|
27
|
+
return dialog;
|
|
28
|
+
}
|
|
29
|
+
if (isCustomTarget(config, dialog.option.target)) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const current = semanticMap[targetLocalId(dialog.option.target)];
|
|
33
|
+
return current
|
|
34
|
+
? { kind: "semantic", option: dialog.option, rule: semanticRuleFor(dialog.option, current) }
|
|
35
|
+
: null;
|
|
36
|
+
}
|
|
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.
|
|
41
|
+
function dialogKey(dialog) {
|
|
42
|
+
return dialog.kind === "semantic"
|
|
43
|
+
? `${dialog.rule.id}:${JSON.stringify(dialog.rule.conditions)}`
|
|
44
|
+
: dialog.rule.id;
|
|
45
|
+
}
|
|
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
|
+
] }));
|
|
52
|
+
}
|
|
21
53
|
export function ConditionalFormattingSection({ properties, propertiesMeta, insight, targetData, backend, workspace, dateSettings, separators, isLoading, pushData, }) {
|
|
22
54
|
const intl = useIntl();
|
|
23
55
|
const [dialog, setDialog] = useState(null);
|
|
@@ -27,6 +59,22 @@ export function ConditionalFormattingSection({ properties, propertiesMeta, insig
|
|
|
27
59
|
const targetOptions = insight ? buildTargetOptions(insight, targetData) : [];
|
|
28
60
|
// Only date pickers consume the catalog — skip the backend query when no target is date-eligible.
|
|
29
61
|
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
|
+
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);
|
|
72
|
+
// `resolveActiveDialog` only hides an invalidated semantic dialog from render — without also
|
|
73
|
+
// clearing the underlying state here, a later data view that happens to carry a rule for the same
|
|
74
|
+
// (possibly stale) target would silently reopen it, with no new View click.
|
|
75
|
+
if (dialog?.kind === "semantic" && activeDialog === null) {
|
|
76
|
+
setDialog(null);
|
|
77
|
+
}
|
|
30
78
|
// Completeness and authoring both need execution-resolved date metadata; before the first data view
|
|
31
79
|
// it's unknown — don't flash a false "Invalid" badge, don't author a plain-text rule on a date attr.
|
|
32
80
|
const targetDataReady = targetData?.dates !== undefined;
|
|
@@ -54,13 +102,24 @@ export function ConditionalFormattingSection({ properties, propertiesMeta, insig
|
|
|
54
102
|
edit: intl.formatMessage(conditionalFormattingMessages.ruleEdit),
|
|
55
103
|
delete: intl.formatMessage(conditionalFormattingMessages.ruleDelete),
|
|
56
104
|
};
|
|
105
|
+
const viewLabel = intl.formatMessage(conditionalFormattingMessages.ruleView);
|
|
57
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: [
|
|
58
107
|
_jsx("span", { className: "s-cf-popover-anchor gd-cf-section__anchor" }), _jsxs("div", { className: "gd-cf-section__subcategory", children: [
|
|
59
|
-
_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({ rule: newRule(targetOptions[0]), isNew: true }) })
|
|
60
|
-
] }), rules.length === 0 ? (
|
|
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) => {
|
|
61
112
|
const option = findTargetOption(targetOptions, rule.target);
|
|
62
113
|
// A missing target flags immediately; value validation waits for date metadata.
|
|
63
114
|
const invalid = !option || (targetDataReady && !isRuleComplete(rule, option.date));
|
|
64
|
-
return (_jsx(RuleChip, { rule: rule, option: option, invalid: invalid, editable: targetDataReady, labels: chipLabels, slot: slot, onEdit: () => setDialog({ rule, isNew: false }), onDelete: () => deleteRule(rule.id) }));
|
|
65
|
-
} }) })),
|
|
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: [
|
|
117
|
+
_jsxs("div", { className: "gd-cf-section__subcategory", children: [
|
|
118
|
+
_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({
|
|
120
|
+
kind: "semantic",
|
|
121
|
+
rule: semanticRuleFor(option, semantic),
|
|
122
|
+
option,
|
|
123
|
+
}) }, 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] }));
|
|
66
125
|
}
|
package/esm/internal/components/configurationControls/conditionalFormatting/ReorderList.d.ts
CHANGED
|
@@ -14,12 +14,14 @@ export interface IReorderListProps<T> {
|
|
|
14
14
|
/** Receives the full reordered array. */
|
|
15
15
|
onReorder: (items: T[]) => void;
|
|
16
16
|
renderItem: (item: T, slot: IReorderSlot, index: number) => ReactNode;
|
|
17
|
+
/** Suppresses drag-to-reorder regardless of item count (e.g. a read-only view of the list). */
|
|
18
|
+
disabled?: boolean;
|
|
17
19
|
}
|
|
18
20
|
/**
|
|
19
21
|
* Drag-to-reorder for a small vertical list, on native HTML5 DnD (no dependency, no provider).
|
|
20
22
|
* The render prop keeps the consumer's own row markup; the whole row drags (the grip is a hover
|
|
21
23
|
* affordance). Pointer-only — no keyboard reorder yet.
|
|
22
24
|
*/
|
|
23
|
-
export declare function ReorderList<T>({ items, getKey, onReorder, renderItem }: IReorderListProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
25
|
+
export declare function ReorderList<T>({ items, getKey, onReorder, renderItem, disabled }: IReorderListProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
24
26
|
export {};
|
|
25
27
|
//# sourceMappingURL=ReorderList.d.ts.map
|
package/esm/internal/components/configurationControls/conditionalFormatting/ReorderList.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReorderList.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/configurationControls/conditionalFormatting/ReorderList.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAY,KAAK,cAAc,EAAE,KAAK,SAAS,EAAoB,MAAM,OAAO,CAAC;AAMxF,KAAK,iBAAiB,GAAG,IAAI,CACzB,cAAc,CAAC,WAAW,CAAC,EACzB,WAAW,GACX,eAAe,GACf,aAAa,GACb,aAAa,GACb,YAAY,GACZ,aAAa,GACb,QAAQ,GACR,WAAW,CAChB,CAAC;AAEF,MAAM,WAAW,YAAY;IACzB,oFAAoF;IACpF,MAAM,EAAE,SAAS,CAAC;IAClB,wCAAwC;IACxC,SAAS,EAAE,iBAAiB,CAAC;IAC7B,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAChC,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;IACpB,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC;IAC5B,yCAAyC;IACzC,SAAS,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,IAAI,CAAC;IAChC,UAAU,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"ReorderList.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/configurationControls/conditionalFormatting/ReorderList.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAY,KAAK,cAAc,EAAE,KAAK,SAAS,EAAoB,MAAM,OAAO,CAAC;AAMxF,KAAK,iBAAiB,GAAG,IAAI,CACzB,cAAc,CAAC,WAAW,CAAC,EACzB,WAAW,GACX,eAAe,GACf,aAAa,GACb,aAAa,GACb,YAAY,GACZ,aAAa,GACb,QAAQ,GACR,WAAW,CAChB,CAAC;AAEF,MAAM,WAAW,YAAY;IACzB,oFAAoF;IACpF,MAAM,EAAE,SAAS,CAAC;IAClB,wCAAwC;IACxC,SAAS,EAAE,iBAAiB,CAAC;IAC7B,6DAA6D;IAC7D,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAChC,KAAK,EAAE,SAAS,CAAC,EAAE,CAAC;IACpB,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,CAAC;IAC5B,yCAAyC;IACzC,SAAS,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,IAAI,CAAC;IAChC,UAAU,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC;IACtE,+FAA+F;IAC/F,QAAQ,CAAC,EAAE,OAAO,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,EAC3B,KAAK,EACL,MAAM,EACN,SAAS,EACT,UAAU,EACV,QAAgB,EACnB,EAAE,iBAAiB,CAAC,CAAC,CAAC,2CA6FtB"}
|
|
@@ -8,13 +8,13 @@ import { moveItem, resolveTarget, startsInFormField } from "./reorderListModel.j
|
|
|
8
8
|
* The render prop keeps the consumer's own row markup; the whole row drags (the grip is a hover
|
|
9
9
|
* affordance). Pointer-only — no keyboard reorder yet.
|
|
10
10
|
*/
|
|
11
|
-
export function ReorderList({ items, getKey, onReorder, renderItem }) {
|
|
11
|
+
export function ReorderList({ items, getKey, onReorder, renderItem, disabled = false, }) {
|
|
12
12
|
const [dragIndex, setDragIndex] = useState(null);
|
|
13
13
|
// Insert gap (0..n), canonical: one gap = one indicator position.
|
|
14
14
|
const [dropGap, setDropGap] = useState(null);
|
|
15
15
|
// Enter/leave counter across all rows; at 0 the cursor is outside every row → hide the indicator.
|
|
16
16
|
const overCount = useRef(0);
|
|
17
|
-
const enabled = items.length > 1;
|
|
17
|
+
const enabled = items.length > 1 && !disabled;
|
|
18
18
|
const reset = () => {
|
|
19
19
|
setDragIndex(null);
|
|
20
20
|
setDropGap(null);
|
|
@@ -1,21 +1,31 @@
|
|
|
1
|
-
import { type DateFilterGranularity, type IInsightDefinition, type WeekStart } from "@gooddata/sdk-model";
|
|
1
|
+
import { type DateFilterGranularity, type IInsightDefinition, type ISemanticConditionalFormatting, type WeekStart } from "@gooddata/sdk-model";
|
|
2
2
|
import { type DataViewFacade } from "@gooddata/sdk-ui";
|
|
3
|
-
import { type ConditionalFormattingOperator, type ConditionalFormattingTarget, type ConditionalFormattingValue, type IConditionalFormattingCondition, type IConditionalFormattingRule } from "@gooddata/sdk-ui-pivot/next";
|
|
3
|
+
import { type ConditionalFormattingOperator, type ConditionalFormattingTarget, type ConditionalFormattingValue, type IConditionalFormatting, type IConditionalFormattingCondition, type IConditionalFormattingRule } from "@gooddata/sdk-ui-pivot/next";
|
|
4
4
|
/**
|
|
5
5
|
* Date-attribute metadata date conditions author and validate against. Present only for granularities
|
|
6
6
|
* the evaluation engine supports (linear; fiscal joins once its labeling convention is verified).
|
|
7
|
+
*
|
|
8
|
+
* @internal
|
|
7
9
|
*/
|
|
8
10
|
export interface ICfDateMeta {
|
|
9
11
|
granularity: DateFilterGranularity;
|
|
10
12
|
/** The column's timezone (descriptor `format.timezone`) — "today" resolves in it. */
|
|
11
13
|
timezone?: string;
|
|
12
14
|
}
|
|
13
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Workspace date-display settings the date value picker honors (derived by the host from settings).
|
|
17
|
+
*
|
|
18
|
+
* @internal
|
|
19
|
+
*/
|
|
14
20
|
export interface ICfDateSettings {
|
|
15
21
|
dateFormat?: string;
|
|
16
22
|
weekStart?: WeekStart;
|
|
17
23
|
}
|
|
18
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* A selectable measure/attribute for the rule's "applies to" dropdown.
|
|
26
|
+
*
|
|
27
|
+
* @internal
|
|
28
|
+
*/
|
|
19
29
|
export interface ITargetOption {
|
|
20
30
|
/** `measure:<localId>` | `attribute:<localId>` */
|
|
21
31
|
value: string;
|
|
@@ -28,6 +38,7 @@ export interface ITargetOption {
|
|
|
28
38
|
/** Present iff the attribute is a date-condition-eligible date attribute; switches the condition set. */
|
|
29
39
|
date?: ICfDateMeta;
|
|
30
40
|
}
|
|
41
|
+
export declare const targetToValue: (target: ConditionalFormattingTarget) => string;
|
|
31
42
|
/** Execution-resolved data the CF UI needs per localId; flows as one object from the pluggable down. */
|
|
32
43
|
export interface ICfTargetData {
|
|
33
44
|
/** Data-view titles by localId; preferred over the insight's often-absent titles. Empty until first load. */
|
|
@@ -38,6 +49,8 @@ export interface ICfTargetData {
|
|
|
38
49
|
elements?: Record<string, string[]>;
|
|
39
50
|
/** Date metadata by localId for date-condition-eligible attributes (descriptor granularity/timezone). */
|
|
40
51
|
dates?: Record<string, ICfDateMeta>;
|
|
52
|
+
/** Semantic-layer conditional formatting carried on the execution result header, by localId. */
|
|
53
|
+
semantic?: Record<string, ISemanticConditionalFormatting>;
|
|
41
54
|
}
|
|
42
55
|
/**
|
|
43
56
|
* Extracts {@link ICfTargetData} from the executed data view. Titles must come from here — attribute
|
|
@@ -50,6 +63,21 @@ export declare const findTargetOption: (options: ITargetOption[], target: Condit
|
|
|
50
63
|
/** Date-eligible target: the option carries execution-resolved date metadata. */
|
|
51
64
|
export declare const isDateTarget: (option: ITargetOption | undefined) => boolean;
|
|
52
65
|
export declare const targetLocalId: (target: ConditionalFormattingTarget) => string;
|
|
66
|
+
/**
|
|
67
|
+
* 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
|
|
69
|
+
* sdk-ui-pivot's `resolvePerTargetConditionalFormatting` (semanticConditionalFormatting.ts); the two
|
|
70
|
+
* 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.
|
|
73
|
+
*/
|
|
74
|
+
export declare const isCustomTarget: (config: IConditionalFormatting | undefined, target: ConditionalFormattingTarget) => boolean;
|
|
75
|
+
/**
|
|
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 only — it's never persisted or compared.
|
|
79
|
+
*/
|
|
80
|
+
export declare const semanticRuleFor: (option: ITargetOption, semantic: ISemanticConditionalFormatting) => IConditionalFormattingRule;
|
|
53
81
|
export declare const operatorsForTarget: (kind: "attribute" | "measure", isDate: boolean) => ConditionalFormattingOperator[];
|
|
54
82
|
export declare const operatorIcon: (operator: ConditionalFormattingOperator) => string | undefined;
|
|
55
83
|
export declare const targetIcon: (kind: "attribute" | "measure", isDate?: boolean) => string;
|
|
@@ -62,6 +90,15 @@ export type ConditionValueEditor = "none" | "number" | "combobox" | "text" | "ra
|
|
|
62
90
|
* operand is always the period picker.
|
|
63
91
|
*/
|
|
64
92
|
export declare const valueEditorKind: (condition: IConditionalFormattingCondition, kind: "attribute" | "measure", hasSuggestions: boolean, isDate: boolean) => ConditionValueEditor;
|
|
93
|
+
/**
|
|
94
|
+
* Whether a stored value's shape is one `editorKind`'s control can represent. Editing always keeps
|
|
95
|
+
* this true (operator changes normalize the value's shape, and {@link sanitizeRuleForEditing} clears
|
|
96
|
+
* what it can't fix), but a read-only semantic-layer rule may pair an operator with a value shape the
|
|
97
|
+
* pivot editor never authors — e.g. a text operator stored against a date target.
|
|
98
|
+
*/
|
|
99
|
+
export declare const valueMatchesEditorKind: (editorKind: ConditionValueEditor, value: ConditionalFormattingValue) => boolean;
|
|
100
|
+
/** A plain-text rendering of a stored value, for the read-only fallback when it doesn't fit its editor. */
|
|
101
|
+
export declare const rawValueText: (value: ConditionalFormattingValue) => string;
|
|
65
102
|
/** Fresh empty operand of the right shape for the operator. */
|
|
66
103
|
export declare const emptyValueForOperator: (operator: ConditionalFormattingOperator, isDate?: boolean) => ConditionalFormattingValue;
|
|
67
104
|
/**
|
|
@@ -99,10 +136,11 @@ export type ConditionInvalidError = "rangeOrder" | "dateUnresolvable";
|
|
|
99
136
|
/** Everything the editor renders inline: the model's errors plus its own blank-visited-field state. */
|
|
100
137
|
export type ConditionalFormattingFieldError = ConditionInvalidError | "valueEmpty";
|
|
101
138
|
/**
|
|
102
|
-
* Coerces stored condition shapes the editor cannot represent so an AAC-authored
|
|
103
|
-
* instead of dead-ended (disabled Save then covers the cleared
|
|
104
|
-
*
|
|
105
|
-
*
|
|
139
|
+
* Coerces stored condition shapes the editor cannot represent so an AAC-authored (or inherited
|
|
140
|
+
* semantic-layer) rule opens editable instead of dead-ended (disabled Save then covers the cleared
|
|
141
|
+
* values). An operator outside the target's curated set resets to "Is on"/"Equal to" and its value
|
|
142
|
+
* shape follows; a non-numeric measure literal clears to empty. Id and format always survive. The
|
|
143
|
+
* engine keeps evaluating the stored original until Save.
|
|
106
144
|
*/
|
|
107
145
|
export declare const sanitizeRuleForEditing: (rule: IConditionalFormattingRule, isDate?: boolean) => IConditionalFormattingRule;
|
|
108
146
|
export interface IConditionValidation {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conditionalFormattingModel.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/configurationControls/conditionalFormatting/conditionalFormattingModel.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,KAAK,qBAAqB,EAC1B,KAAK,kBAAkB,EACvB,KAAK,SAAS,EAajB,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,EAIlC,MAAM,6BAA6B,CAAC;AAIrC
|
|
1
|
+
{"version":3,"file":"conditionalFormattingModel.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/configurationControls/conditionalFormatting/conditionalFormattingModel.ts"],"names":[],"mappings":"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"}
|