@gooddata/sdk-ui-ext 11.53.0-alpha.0 → 11.53.0-alpha.2
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 +1 -1
- package/esm/insightView/InsightRenderer.d.ts.map +1 -1
- package/esm/insightView/InsightRenderer.js +36 -9
- package/esm/internal/components/BaseVisualization.d.ts +6 -33
- package/esm/internal/components/BaseVisualization.d.ts.map +1 -1
- package/esm/internal/components/BaseVisualization.js +255 -213
- 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/index.d.ts +1 -1
- package/esm/internal/index.d.ts.map +1 -1
- package/esm/internal/index.js +1 -1
- 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
|
@@ -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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"}
|
|
@@ -6,7 +6,7 @@ import { isDateConditionValue, normalizeDateConditionGranularity, resolveDateCon
|
|
|
6
6
|
import { CF_DEFAULT_COLOR } from "./conditionalFormattingColors.js";
|
|
7
7
|
// Cap suggestions so a high-cardinality attribute can't build a huge suggestion list.
|
|
8
8
|
const MAX_ELEMENT_SUGGESTIONS = 200;
|
|
9
|
-
const targetToValue = (target) => target.kind === "measure"
|
|
9
|
+
export const targetToValue = (target) => target.kind === "measure"
|
|
10
10
|
? `measure:${target.measureIdentifier}`
|
|
11
11
|
: `attribute:${target.attributeIdentifier}`;
|
|
12
12
|
/**
|
|
@@ -18,15 +18,19 @@ export function buildCfTargetData(dataView) {
|
|
|
18
18
|
const titles = {};
|
|
19
19
|
const formats = {};
|
|
20
20
|
const dates = {};
|
|
21
|
+
const semantic = {};
|
|
21
22
|
for (const measure of dataView.meta().measureDescriptors()) {
|
|
22
|
-
const { localIdentifier, name, format } = measure.measureHeaderItem;
|
|
23
|
+
const { localIdentifier, name, format, conditionalFormatting } = measure.measureHeaderItem;
|
|
23
24
|
titles[localIdentifier] = name;
|
|
24
25
|
if (format) {
|
|
25
26
|
formats[localIdentifier] = format;
|
|
26
27
|
}
|
|
28
|
+
if (conditionalFormatting) {
|
|
29
|
+
semantic[localIdentifier] = conditionalFormatting;
|
|
30
|
+
}
|
|
27
31
|
}
|
|
28
32
|
for (const attribute of dataView.meta().attributeDescriptors()) {
|
|
29
|
-
const { localIdentifier, formOf, granularity, format } = attribute.attributeHeader;
|
|
33
|
+
const { localIdentifier, formOf, granularity, format, conditionalFormatting } = attribute.attributeHeader;
|
|
30
34
|
titles[localIdentifier] = formOf.name;
|
|
31
35
|
// Eligible date attributes (resolvable granularity) get date conditions; the rest keep plain text.
|
|
32
36
|
const normalizedGranularity = normalizeDateConditionGranularity(granularity);
|
|
@@ -36,8 +40,11 @@ export function buildCfTargetData(dataView) {
|
|
|
36
40
|
...(format?.timezone ? { timezone: format.timezone } : {}),
|
|
37
41
|
};
|
|
38
42
|
}
|
|
43
|
+
if (conditionalFormatting) {
|
|
44
|
+
semantic[localIdentifier] = conditionalFormatting;
|
|
45
|
+
}
|
|
39
46
|
}
|
|
40
|
-
return { titles, formats, elements: buildElementsByLocalId(dataView), dates };
|
|
47
|
+
return { titles, formats, elements: buildElementsByLocalId(dataView), dates, semantic };
|
|
41
48
|
}
|
|
42
49
|
// Attribute header groups align by index with the dimension's attribute descriptors; the collected
|
|
43
50
|
// elements are only as complete as the loaded (paged) result.
|
|
@@ -109,6 +116,29 @@ export const findTargetOption = (options, target) => options.find((option) => op
|
|
|
109
116
|
/** Date-eligible target: the option carries execution-resolved date metadata. */
|
|
110
117
|
export const isDateTarget = (option) => option?.date !== undefined;
|
|
111
118
|
export const targetLocalId = (target) => target.kind === "measure" ? target.measureIdentifier : target.attributeIdentifier;
|
|
119
|
+
/**
|
|
120
|
+
* Whether `target` is already in Custom mode on this insight config — an insight rule targets it,
|
|
121
|
+
* or it's explicitly listed in `customTargets`. Mirrors the `customModeTargets` set built by
|
|
122
|
+
* sdk-ui-pivot's `resolvePerTargetConditionalFormatting` (semanticConditionalFormatting.ts); the two
|
|
123
|
+
* packages don't share this helper, so keep them in sync by hand if either changes. Honoring
|
|
124
|
+
* `customTargets` here matters even though this PR never writes it: if some other path already
|
|
125
|
+
* marked a target Custom, the semantic block must still hide it as Inherited would be wrong.
|
|
126
|
+
*/
|
|
127
|
+
export const isCustomTarget = (config, target) => {
|
|
128
|
+
const value = targetToValue(target);
|
|
129
|
+
return ((config?.rules ?? []).some((rule) => targetToValue(rule.target) === value) ||
|
|
130
|
+
(config?.customTargets ?? []).some((customTarget) => targetToValue(customTarget) === value));
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Synthesizes a read-only rule for the View dialog from an inherited semantic-layer condition set.
|
|
134
|
+
* The id mirrors the engine's own `semantic:<kind>:<localId>` convention (semanticConditionalFormatting.ts)
|
|
135
|
+
* for consistency only — it's never persisted or compared.
|
|
136
|
+
*/
|
|
137
|
+
export const semanticRuleFor = (option, semantic) => ({
|
|
138
|
+
id: `semantic:${option.target.kind}:${targetLocalId(option.target)}`,
|
|
139
|
+
target: option.target,
|
|
140
|
+
conditions: semantic.conditions,
|
|
141
|
+
});
|
|
112
142
|
// --- Operators -------------------------------------------------------------------------------
|
|
113
143
|
const NO_VALUE_OPERATORS = new Set([
|
|
114
144
|
"ALL",
|
|
@@ -214,6 +244,43 @@ export const valueEditorKind = (condition, kind, hasSuggestions, isDate) => {
|
|
|
214
244
|
return hasSuggestions && isEqualityOperator(condition.operator) ? "combobox" : "text";
|
|
215
245
|
}
|
|
216
246
|
};
|
|
247
|
+
/**
|
|
248
|
+
* Whether a stored value's shape is one `editorKind`'s control can represent. Editing always keeps
|
|
249
|
+
* this true (operator changes normalize the value's shape, and {@link sanitizeRuleForEditing} clears
|
|
250
|
+
* what it can't fix), but a read-only semantic-layer rule may pair an operator with a value shape the
|
|
251
|
+
* pivot editor never authors — e.g. a text operator stored against a date target.
|
|
252
|
+
*/
|
|
253
|
+
export const valueMatchesEditorKind = (editorKind, value) => {
|
|
254
|
+
switch (editorKind) {
|
|
255
|
+
case "none":
|
|
256
|
+
return value.kind === "none";
|
|
257
|
+
case "date":
|
|
258
|
+
return value.kind === "none" || value.kind === "absoluteDate" || value.kind === "relativeDate";
|
|
259
|
+
case "range":
|
|
260
|
+
return value.kind === "none" || value.kind === "literalRange";
|
|
261
|
+
case "number":
|
|
262
|
+
return (value.kind === "none" ||
|
|
263
|
+
(value.kind === "literal" && (isBlank(value.value) || Number.isFinite(Number(value.value)))));
|
|
264
|
+
case "combobox":
|
|
265
|
+
case "text":
|
|
266
|
+
return value.kind === "none" || value.kind === "literal";
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
/** A plain-text rendering of a stored value, for the read-only fallback when it doesn't fit its editor. */
|
|
270
|
+
export const rawValueText = (value) => {
|
|
271
|
+
switch (value.kind) {
|
|
272
|
+
case "none":
|
|
273
|
+
return "";
|
|
274
|
+
case "literal":
|
|
275
|
+
return String(value.value);
|
|
276
|
+
case "literalRange":
|
|
277
|
+
return `${value.from} – ${value.to}`;
|
|
278
|
+
case "absoluteDate":
|
|
279
|
+
return `${value.from} – ${value.to}`;
|
|
280
|
+
case "relativeDate":
|
|
281
|
+
return `${value.granularity} ${value.from} … ${value.to}`;
|
|
282
|
+
}
|
|
283
|
+
};
|
|
217
284
|
// --- Factories -------------------------------------------------------------------------------
|
|
218
285
|
/** Fresh empty operand of the right shape for the operator. */
|
|
219
286
|
export const emptyValueForOperator = (operator, isDate = false) => {
|
|
@@ -342,10 +409,11 @@ export const rawToDisplayNumber = (raw, percent) => percent ? denoise(raw * 100)
|
|
|
342
409
|
export const displayToRawNumber = (display, percent) => percent ? denoise(display / 100) : display;
|
|
343
410
|
const isBlank = (value) => String(value).trim() === "";
|
|
344
411
|
/**
|
|
345
|
-
* Coerces stored condition shapes the editor cannot represent so an AAC-authored
|
|
346
|
-
* instead of dead-ended (disabled Save then covers the cleared
|
|
347
|
-
*
|
|
348
|
-
*
|
|
412
|
+
* Coerces stored condition shapes the editor cannot represent so an AAC-authored (or inherited
|
|
413
|
+
* semantic-layer) rule opens editable instead of dead-ended (disabled Save then covers the cleared
|
|
414
|
+
* values). An operator outside the target's curated set resets to "Is on"/"Equal to" and its value
|
|
415
|
+
* shape follows; a non-numeric measure literal clears to empty. Id and format always survive. The
|
|
416
|
+
* engine keeps evaluating the stored original until Save.
|
|
349
417
|
*/
|
|
350
418
|
export const sanitizeRuleForEditing = (rule, isDate = false) => {
|
|
351
419
|
if (rule.target.kind === "attribute" && isDate) {
|
|
@@ -367,16 +435,26 @@ export const sanitizeRuleForEditing = (rule, isDate = false) => {
|
|
|
367
435
|
}),
|
|
368
436
|
};
|
|
369
437
|
}
|
|
370
|
-
|
|
371
|
-
return rule;
|
|
372
|
-
}
|
|
438
|
+
const curatedOperators = operatorsForTarget(rule.target.kind, false);
|
|
373
439
|
return {
|
|
374
440
|
...rule,
|
|
375
|
-
conditions: rule.conditions.map((condition) =>
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
?
|
|
379
|
-
|
|
441
|
+
conditions: rule.conditions.map((condition) => {
|
|
442
|
+
const operatorOk = curatedOperators.includes(condition.operator);
|
|
443
|
+
const operator = operatorOk ? condition.operator : "EQUAL_TO";
|
|
444
|
+
const value = operatorOk ? condition.value : valueForOperator(operator, condition.value, false);
|
|
445
|
+
const numericLiteralOk = rule.target.kind !== "measure" ||
|
|
446
|
+
value.kind !== "literal" ||
|
|
447
|
+
isBlank(value.value) ||
|
|
448
|
+
!Number.isNaN(Number(value.value));
|
|
449
|
+
if (operatorOk && numericLiteralOk) {
|
|
450
|
+
return condition;
|
|
451
|
+
}
|
|
452
|
+
return {
|
|
453
|
+
...condition,
|
|
454
|
+
operator,
|
|
455
|
+
value: numericLiteralOk ? value : { kind: "literal", value: "" },
|
|
456
|
+
};
|
|
457
|
+
}),
|
|
380
458
|
};
|
|
381
459
|
};
|
|
382
460
|
/**
|
|
@@ -5,6 +5,8 @@ import { type IDateFilterOptionsByType } from "@gooddata/sdk-ui-filters";
|
|
|
5
5
|
* dashboard date filter consumes. Undefined while loading (the picker degrades to its static form);
|
|
6
6
|
* falls back to platform defaults when backend/workspace is absent, the workspace has no custom
|
|
7
7
|
* config, the query fails, or the config yields nothing visible.
|
|
8
|
+
*
|
|
9
|
+
* @internal
|
|
8
10
|
*/
|
|
9
11
|
export declare function useCfDateFilterOptions(backend: IAnalyticalBackend | undefined, workspace: string | undefined,
|
|
10
12
|
/** False = no consumer in sight (no date-eligible target) — skip the backend query entirely. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCfDateFilterOptions.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/configurationControls/conditionalFormatting/useCfDateFilterOptions.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAEpE,OAAO,EACH,KAAK,wBAAwB,EAIhC,MAAM,0BAA0B,CAAC;AAMlC
|
|
1
|
+
{"version":3,"file":"useCfDateFilterOptions.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/configurationControls/conditionalFormatting/useCfDateFilterOptions.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAEpE,OAAO,EACH,KAAK,wBAAwB,EAIhC,MAAM,0BAA0B,CAAC;AAMlC;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAClC,OAAO,EAAE,kBAAkB,GAAG,SAAS,EACvC,SAAS,EAAE,MAAM,GAAG,SAAS;AAC7B,kGAAgG;AAChG,OAAO,EAAE,OAAO,GACjB,wBAAwB,GAAG,SAAS,CAkCtC"}
|
|
@@ -9,6 +9,8 @@ const FALLBACK_DATE_FILTER_OPTIONS = convertDateFilterConfigToDateFilterOptions(
|
|
|
9
9
|
* dashboard date filter consumes. Undefined while loading (the picker degrades to its static form);
|
|
10
10
|
* falls back to platform defaults when backend/workspace is absent, the workspace has no custom
|
|
11
11
|
* config, the query fails, or the config yields nothing visible.
|
|
12
|
+
*
|
|
13
|
+
* @internal
|
|
12
14
|
*/
|
|
13
15
|
export function useCfDateFilterOptions(backend, workspace,
|
|
14
16
|
/** False = no consumer in sight (no date-eligible target) — skip the backend query entirely. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PluggablePivotTableNext.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/pluggableVisualizations/pivotTableNext/PluggablePivotTableNext.tsx"],"names":[],"mappings":"AAIA,OAAO,EAGH,KAAK,iBAAiB,EACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACH,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,kBAAkB,EACvB,KAAK,SAAS,EAYjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAIH,KAAK,wBAAwB,EAEhC,MAAM,kBAAkB,CAAC;AAM1B,OAAO,EAGH,KAAK,oBAAoB,EAE5B,MAAM,6BAA6B,CAAC;AAKrC,OAAO,EAEH,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,wBAAwB,EAGhC,MAAM,sCAAsC,CAAC;AAoC9C,OAAO,EAAE,8BAA8B,EAAE,MAAM,sCAAsC,CAAC;AAiBtF,wBAAgB,0BAA0B,CACtC,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,wBAAwB,EACrC,QAAQ,EAAE,SAAS,GACpB,oBAAoB,CAoBtB;AAED;;;;;GAKG;AACH,qBAAa,uBAAwB,SAAQ,8BAA8B;IACvE,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAY;IACrC,OAAO,CAAC,mBAAmB,CAAuB;IAElD,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,cAAc,CAAkB;IACxC,OAAO,CAAC,WAAW,CAAuC;IAE1D,OAAO,CAAC,YAAY,CAAqB;IAEzC,YAAY,KAAK,EAAE,aAAa,EAqB/B;IAEM,OAAO,IAAI,IAAI,CAErB;IAEM,yBAAyB,CAC5B,cAAc,EAAE,eAAe,EAC/B,sBAAsB,CAAC,EAAE,eAAe,GACzC,OAAO,CAAC,uBAAuB,CAAC,CA8GlC;IAEe,8BAA8B,CAC1C,mBAAmB,EAAE,QAAQ,EAC7B,gBAAgB,EAAE,iBAAiB,EACnC,0BAA0B,EAAE,OAAO,GACpC,QAAQ,CAWV;IAED,OAAO,CAAC,kBAAkB;IAInB,YAAY,CACf,OAAO,EAAE,SAAS,EAClB,OAAO,EAAE,kBAAkB,EAC3B,gBAAgB,EAAE,iBAAiB,0DActC;IAED,SAAS,CAAC,oBAAoB,CAAC,uBAAuB,EAAE,wBAAwB,GAAG,IAAI,CActF;IAKD,OAAO,CAAC,cAAc,CAIpB;IAIF,OAAO,CAAC,oBAAoB,
|
|
1
|
+
{"version":3,"file":"PluggablePivotTableNext.d.ts","sourceRoot":"","sources":["../../../../../src/internal/components/pluggableVisualizations/pivotTableNext/PluggablePivotTableNext.tsx"],"names":[],"mappings":"AAIA,OAAO,EAGH,KAAK,iBAAiB,EACzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACH,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,kBAAkB,EACvB,KAAK,SAAS,EAYjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAIH,KAAK,wBAAwB,EAEhC,MAAM,kBAAkB,CAAC;AAM1B,OAAO,EAGH,KAAK,oBAAoB,EAE5B,MAAM,6BAA6B,CAAC;AAKrC,OAAO,EAEH,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,UAAU,EACf,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,wBAAwB,EAGhC,MAAM,sCAAsC,CAAC;AAoC9C,OAAO,EAAE,8BAA8B,EAAE,MAAM,sCAAsC,CAAC;AAiBtF,wBAAgB,0BAA0B,CACtC,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,wBAAwB,EACrC,QAAQ,EAAE,SAAS,GACpB,oBAAoB,CAoBtB;AAED;;;;;GAKG;AACH,qBAAa,uBAAwB,SAAQ,8BAA8B;IACvE,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAY;IACrC,OAAO,CAAC,mBAAmB,CAAuB;IAElD,OAAO,CAAC,SAAS,CAAqB;IACtC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,cAAc,CAAkB;IACxC,OAAO,CAAC,WAAW,CAAuC;IAE1D,OAAO,CAAC,YAAY,CAAqB;IAEzC,YAAY,KAAK,EAAE,aAAa,EAqB/B;IAEM,OAAO,IAAI,IAAI,CAErB;IAEM,yBAAyB,CAC5B,cAAc,EAAE,eAAe,EAC/B,sBAAsB,CAAC,EAAE,eAAe,GACzC,OAAO,CAAC,uBAAuB,CAAC,CA8GlC;IAEe,8BAA8B,CAC1C,mBAAmB,EAAE,QAAQ,EAC7B,gBAAgB,EAAE,iBAAiB,EACnC,0BAA0B,EAAE,OAAO,GACpC,QAAQ,CAWV;IAED,OAAO,CAAC,kBAAkB;IAInB,YAAY,CACf,OAAO,EAAE,SAAS,EAClB,OAAO,EAAE,kBAAkB,EAC3B,gBAAgB,EAAE,iBAAiB,0DActC;IAED,SAAS,CAAC,oBAAoB,CAAC,uBAAuB,EAAE,wBAAwB,GAAG,IAAI,CActF;IAKD,OAAO,CAAC,cAAc,CAIpB;IAIF,OAAO,CAAC,oBAAoB,CAU1B;IAEF,OAAO,CAAC,yBAAyB,CAe/B;IAEF,UAAmB,wBAAwB,CACvC,OAAO,EAAE,SAAS,EAClB,OAAO,EAAE,kBAAkB,EAC3B,qBAAqB,EAAE,GAAG,GAC3B,IAAI,CASN;IAED,UAAmB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAOzE;IAED,SAAS,CAAC,mBAAmB,CACzB,OAAO,EAAE,SAAS,EAClB,OAAO,EAAE,kBAAkB,EAC3B,gBAAgB,EAAE,iBAAiB,GACpC,IAAI,CAsFN;IAED,SAAS,CAAC,wBAAwB,CAAC,OAAO,EAAE,kBAAkB,EAAE,OAAO,EAAE,SAAS,GAAG,IAAI,CA6BxF;IAED,SAAS,CAAC,aAAa,CAAC,OAAO,EAAE,kBAAkB,EAAE,yBAAyB,CAAC,EAAE,GAAG,GAAG,UAAU,EAAE,CAElG;IAED,OAAO,CAAC,wBAAwB;IAYhC,OAAO,CAAC,oBAAoB;IA8B5B,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,cAAc;CAmCzB"}
|