@gooddata/sdk-ui-dashboard 11.58.0-alpha.0 → 11.58.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 +2322 -1761
- package/esm/__version.d.ts +1 -1
- package/esm/__version.js +1 -1
- package/esm/_staging/sharedHooks/useFiltersNamings.d.ts.map +1 -1
- package/esm/_staging/sharedHooks/useFiltersNamings.js +5 -3
- package/esm/index.d.ts +5 -5
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +3 -3
- package/esm/model/commandHandlers/dashboard/common/parameterHydration.d.ts +2 -2
- package/esm/model/commandHandlers/dashboard/common/parameterHydration.d.ts.map +1 -1
- package/esm/model/commandHandlers/dashboard/common/parameterHydration.js +2 -5
- package/esm/model/commandHandlers/filterContext/attributeFilter/addAttributeFilterHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/filterContext/attributeFilter/addAttributeFilterHandler.js +5 -0
- package/esm/model/commandHandlers/filterContext/attributeFilter/addTextAttributeFilterHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/filterContext/attributeFilter/addTextAttributeFilterHandler.js +5 -0
- package/esm/model/commandHandlers/filterContext/attributeFilter/changeAttributeDisplayFormHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/filterContext/attributeFilter/changeAttributeDisplayFormHandler.js +5 -0
- package/esm/model/commandHandlers/filterContext/attributeFilter/validation/computedAttributesEnabledValidation.d.ts +16 -0
- package/esm/model/commandHandlers/filterContext/attributeFilter/validation/computedAttributesEnabledValidation.d.ts.map +1 -0
- package/esm/model/commandHandlers/filterContext/attributeFilter/validation/computedAttributesEnabledValidation.js +27 -0
- package/esm/model/commandHandlers/filterContext/changeFilterContextSelectionHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/filterContext/changeFilterContextSelectionHandler.js +11 -0
- package/esm/model/commandHandlers/filterContext/common.d.ts +1 -1
- package/esm/model/commandHandlers/filterContext/common.d.ts.map +1 -1
- package/esm/model/commandHandlers/filterContext/common.js +5 -0
- package/esm/model/commandHandlers/filterContext/filterViewHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/filterContext/filterViewHandler.js +5 -2
- package/esm/model/commandHandlers/parameters/changeParameterValuesHandler.d.ts +12 -2
- package/esm/model/commandHandlers/parameters/changeParameterValuesHandler.d.ts.map +1 -1
- package/esm/model/commandHandlers/parameters/changeParameterValuesHandler.js +31 -3
- package/esm/model/commands/parameters.d.ts +12 -3
- package/esm/model/commands/parameters.d.ts.map +1 -1
- package/esm/model/events/base.d.ts +1 -1
- package/esm/model/events/base.d.ts.map +1 -1
- package/esm/model/events/index.d.ts +2 -2
- package/esm/model/events/index.d.ts.map +1 -1
- package/esm/model/events/parameters.d.ts +38 -0
- package/esm/model/events/parameters.d.ts.map +1 -1
- package/esm/model/events/parameters.js +22 -0
- package/esm/model/react/useIsWidgetRestricted.d.ts.map +1 -1
- package/esm/model/react/useIsWidgetRestricted.js +14 -7
- package/esm/model/store/tabs/index.d.ts +0 -4
- package/esm/model/store/tabs/index.d.ts.map +1 -1
- package/esm/model/store/tabs/layout/layoutSelectors.d.ts +10 -0
- package/esm/model/store/tabs/layout/layoutSelectors.d.ts.map +1 -1
- package/esm/model/store/tabs/layout/layoutSelectors.js +55 -26
- package/esm/model/store/tabs/parameters/parametersHelpers.d.ts +15 -4
- package/esm/model/store/tabs/parameters/parametersHelpers.d.ts.map +1 -1
- package/esm/model/store/tabs/parameters/parametersHelpers.js +28 -0
- package/esm/model/store/tabs/parameters/parametersReducers.d.ts +2 -12
- package/esm/model/store/tabs/parameters/parametersReducers.d.ts.map +1 -1
- package/esm/model/store/tabs/parameters/parametersReducers.js +0 -4
- package/esm/model/store/tabs/parameters/parametersSelectors.d.ts +22 -4
- package/esm/model/store/tabs/parameters/parametersSelectors.d.ts.map +1 -1
- package/esm/model/store/tabs/parameters/parametersSelectors.js +46 -12
- package/esm/presentation/automations/alerting/types.d.ts +7 -83
- package/esm/presentation/automations/alerting/types.d.ts.map +1 -1
- package/esm/presentation/automations/scheduledEmail/types.d.ts +6 -112
- package/esm/presentation/automations/scheduledEmail/types.d.ts.map +1 -1
- package/esm/presentation/automations/shared/hooks/useFiltersNamings.d.ts.map +1 -1
- package/esm/presentation/automations/shared/hooks/useFiltersNamings.js +5 -3
- package/esm/presentation/filterBar/filterBar/hooks/useResetFiltersButton.js +2 -2
- package/esm/presentation/filterBar/parameterFilter/DashboardParameterFilter.d.ts.map +1 -1
- package/esm/presentation/filterBar/parameterFilter/DashboardParameterFilter.js +2 -1
- package/esm/presentation/flexibleLayout/DefaultDashboardLayoutRenderer/EditableHeader/SectionHeaderEditable.d.ts.map +1 -1
- package/esm/presentation/flexibleLayout/DefaultDashboardLayoutRenderer/EditableHeader/SectionHeaderEditable.js +35 -13
- package/esm/presentation/localization/bundles/en-US.localization-bundle.d.ts +12 -0
- package/esm/presentation/localization/bundles/en-US.localization-bundle.d.ts.map +1 -1
- package/esm/presentation/localization/bundles/en-US.localization-bundle.js +12 -0
- package/esm/presentation/widget/insight/configuration/InsightDescriptionConfig/InsightDescription.d.ts.map +1 -1
- package/esm/presentation/widget/insight/configuration/InsightDescriptionConfig/InsightDescription.js +30 -12
- package/esm/presentation/widget/richText/EditModeDashboardRichText.d.ts.map +1 -1
- package/esm/presentation/widget/richText/EditModeDashboardRichText.js +18 -6
- package/esm/presentation/widget/richText/RestrictedReferencesDialog.d.ts +22 -0
- package/esm/presentation/widget/richText/RestrictedReferencesDialog.d.ts.map +1 -0
- package/esm/presentation/widget/richText/RestrictedReferencesDialog.js +42 -0
- package/esm/presentation/widget/richText/useEditableRichTextMenu.d.ts.map +1 -1
- package/esm/presentation/widget/richText/useEditableRichTextMenu.js +6 -2
- package/esm/sdk-ui-dashboard.d.ts +121 -208
- package/package.json +23 -23
- package/styles/css/main.css +20 -9
- package/styles/css/main.css.map +1 -1
package/esm/presentation/widget/insight/configuration/InsightDescriptionConfig/InsightDescription.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// (C) 2022-2026 GoodData Corporation
|
|
3
3
|
import { useCallback, useEffect, useState } from "react";
|
|
4
4
|
import cx from "classnames";
|
|
@@ -7,6 +7,7 @@ import { RichTextWithTooltip } from "@gooddata/sdk-ui-kit";
|
|
|
7
7
|
import { useDashboardSelector } from "../../../../../model/react/DashboardStoreProvider.js";
|
|
8
8
|
import { useDashboardExecConfig } from "../../../../../model/react/useWidgetExecConfig.js";
|
|
9
9
|
import { selectRestrictedRichTextReferences } from "../../../../../model/store/unavailableObjects/unavailableObjectsSelectors.js";
|
|
10
|
+
import { RestrictedReferencesDialog, useHasRestrictedReferences, } from "../../../richText/RestrictedReferencesDialog.js";
|
|
10
11
|
const richTextTooltipAlignPoints = [
|
|
11
12
|
{ align: "bl tl", offset: { x: 4, y: 5 } },
|
|
12
13
|
{ align: "tl bl", offset: { x: 4, y: -5 } },
|
|
@@ -16,7 +17,8 @@ export function InsightDescription({ description, setDescription, readOnly = fal
|
|
|
16
17
|
const placeholder = intl.formatMessage({
|
|
17
18
|
id: "configurationPanel.visualprops.descriptionPlaceholder",
|
|
18
19
|
});
|
|
19
|
-
|
|
20
|
+
// what the editor asked for; whether it is the question or the editor follows from the text
|
|
21
|
+
const [isEditingRequested, setIsEditingRequested] = useState(false);
|
|
20
22
|
const [richTextValue, setRichTextValue] = useState(description);
|
|
21
23
|
const execConfig = useDashboardExecConfig();
|
|
22
24
|
const restrictedReferences = useDashboardSelector(selectRestrictedRichTextReferences);
|
|
@@ -26,17 +28,33 @@ export function InsightDescription({ description, setDescription, readOnly = fal
|
|
|
26
28
|
const onChange = useCallback((value) => {
|
|
27
29
|
setDescription(value.trim());
|
|
28
30
|
}, [setDescription]);
|
|
29
|
-
const
|
|
30
|
-
if (!isRichTextEditing && !readOnly) {
|
|
31
|
-
setIsRichTextEditing(true);
|
|
32
|
-
}
|
|
33
|
-
}, [isRichTextEditing, readOnly]);
|
|
34
|
-
const onDescriptionBlur = useCallback(() => {
|
|
35
|
-
setIsRichTextEditing(false);
|
|
36
|
-
onChange(richTextValue);
|
|
37
|
-
}, [onChange, richTextValue]);
|
|
31
|
+
const startEditing = useCallback(() => setIsEditingRequested(true), []);
|
|
38
32
|
const onRichTextChange = useCallback((value) => {
|
|
39
33
|
setRichTextValue(value);
|
|
40
34
|
}, []);
|
|
41
|
-
|
|
35
|
+
const hasRestrictedReferences = useHasRestrictedReferences(richTextValue);
|
|
36
|
+
const isRichTextEditing = isEditingRequested && !hasRestrictedReferences;
|
|
37
|
+
const isAskingToSanitize = isEditingRequested && hasRestrictedReferences;
|
|
38
|
+
// the rewrite is committed at once: it was confirmed, and it is not a keystroke. The editor was
|
|
39
|
+
// already asked for, so the description opens as soon as the text no longer holds a reference
|
|
40
|
+
const onSanitized = useCallback((sanitized) => {
|
|
41
|
+
setRichTextValue(sanitized);
|
|
42
|
+
onChange(sanitized);
|
|
43
|
+
}, [onChange]);
|
|
44
|
+
const stopEditing = useCallback(() => setIsEditingRequested(false), []);
|
|
45
|
+
const onDescriptionBlur = useCallback(() => {
|
|
46
|
+
// the confirmation takes the focus out of the description, and that is not the editor
|
|
47
|
+
// leaving it: nothing is decided yet
|
|
48
|
+
if (isAskingToSanitize) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
setIsEditingRequested(false);
|
|
52
|
+
onChange(richTextValue);
|
|
53
|
+
}, [onChange, richTextValue, isAskingToSanitize]);
|
|
54
|
+
const onDescriptionClick = useCallback(() => {
|
|
55
|
+
if (!isEditingRequested && !readOnly) {
|
|
56
|
+
startEditing();
|
|
57
|
+
}
|
|
58
|
+
}, [isEditingRequested, readOnly, startEditing]);
|
|
59
|
+
return (_jsxs("label", { className: "gd-input", children: [_jsx("div", { className: cx("gd-input-field gd-rich-text-insight-description", { disabled: readOnly }), onClick: onDescriptionClick, onBlur: onDescriptionBlur, children: _jsx(RichTextWithTooltip, { value: richTextValue, renderMode: isRichTextEditing ? "edit" : "view", onChange: onRichTextChange, editRows: 4, editPlaceholder: placeholder, emptyElement: readOnly ? undefined : (_jsx("div", { className: "gd-editable-label-richtext-empty", children: placeholder })), showTooltip: isRichTextEditing, tooltipAlignPoints: richTextTooltipAlignPoints, referencesEnabled: true, LoadingComponent: LoadingComponent, filters: insightFilters, separators: separators, restrictedReferences: restrictedReferences, execConfig: execConfig }) }), isAskingToSanitize ? (_jsx(RestrictedReferencesDialog, { value: richTextValue, onSanitized: onSanitized, onCancel: stopEditing })) : null] }));
|
|
42
60
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditModeDashboardRichText.d.ts","sourceRoot":"","sources":["../../../../src/presentation/widget/richText/EditModeDashboardRichText.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"EditModeDashboardRichText.d.ts","sourceRoot":"","sources":["../../../../src/presentation/widget/richText/EditModeDashboardRichText.tsx"],"names":[],"mappings":"AA+BA,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAK1D;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,EAAE,uBAAuB,2CAqIvG"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// (C) 2020-2026 GoodData Corporation
|
|
3
|
-
import { useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
4
4
|
import { FormattedMessage, useIntl } from "react-intl";
|
|
5
5
|
import { widgetRef } from "@gooddata/sdk-model";
|
|
6
6
|
import { usePrevious } from "@gooddata/sdk-ui";
|
|
@@ -13,9 +13,11 @@ import { useDashboardDispatch, useDashboardSelector } from "../../../model/react
|
|
|
13
13
|
import { useDashboardExecConfig } from "../../../model/react/useWidgetExecConfig.js";
|
|
14
14
|
import { useWidgetSelection } from "../../../model/react/useWidgetSelection.js";
|
|
15
15
|
import { selectIsWhiteLabeled, selectSeparators } from "../../../model/store/config/configSelectors.js";
|
|
16
|
+
import { uiActions } from "../../../model/store/ui/index.js";
|
|
16
17
|
import { selectRestrictedRichTextReferences } from "../../../model/store/unavailableObjects/unavailableObjectsSelectors.js";
|
|
17
18
|
import { DASHBOARD_OVERLAYS_FILTER_Z_INDEX } from "../../../presentation/constants/zIndex.js";
|
|
18
19
|
import { useDashboardComponentsContext } from "../../dashboardContexts/DashboardComponentsContext.js";
|
|
20
|
+
import { RestrictedReferencesDialog, useHasRestrictedReferences } from "./RestrictedReferencesDialog.js";
|
|
19
21
|
import { useEditableRichTextMenu } from "./useEditableRichTextMenu.js";
|
|
20
22
|
const overlayController = OverlayController.getInstance(DASHBOARD_OVERLAYS_FILTER_Z_INDEX);
|
|
21
23
|
/**
|
|
@@ -32,8 +34,21 @@ export function EditModeDashboardRichText({ widget, clientWidth, clientHeight })
|
|
|
32
34
|
const { filters } = useRichTextWidgetFilters(widget);
|
|
33
35
|
const separators = useDashboardSelector(selectSeparators);
|
|
34
36
|
const dispatch = useDashboardDispatch();
|
|
37
|
+
const releaseWidget = useCallback(() => dispatch(uiActions.clearWidgetSelection()), [dispatch]);
|
|
35
38
|
const [richText, setRichText] = useState(widget?.content);
|
|
36
|
-
|
|
39
|
+
// the rewrite is committed at once: it was confirmed, and it is not a keystroke
|
|
40
|
+
const onSanitized = useCallback((sanitized) => {
|
|
41
|
+
setRichText(sanitized);
|
|
42
|
+
dispatch(changeRichTextWidgetContent(widget.ref, sanitized));
|
|
43
|
+
}, [dispatch, widget.ref]);
|
|
44
|
+
// Selecting the widget is what opens its editor, and a text holding a reference the editor may
|
|
45
|
+
// not read is asked about first. Both states are that selection and that text, so neither is
|
|
46
|
+
// kept as a flag: confirming rewrites the text, which is what turns the question into the
|
|
47
|
+
// editor; cancelling lets the widget go - deliberately, rather than through deselectWidgets,
|
|
48
|
+
// which holds on to a widget whose text the pointer happens to have selected.
|
|
49
|
+
const hasRestrictedReferences = useHasRestrictedReferences(richText);
|
|
50
|
+
const isRichTextEditing = isSelected && !hasRestrictedReferences;
|
|
51
|
+
const isAskingToSanitize = isSelected && hasRestrictedReferences;
|
|
37
52
|
const [isConfirmDeleteDialogVisible, setIsConfirmDeleteDialogVisible] = useState(false);
|
|
38
53
|
const theme = useTheme();
|
|
39
54
|
const emptyContentIconColor = theme?.palette?.complementary?.c7 ?? "#6D7680";
|
|
@@ -42,9 +57,6 @@ export function EditModeDashboardRichText({ widget, clientWidth, clientHeight })
|
|
|
42
57
|
}, [emptyContentIconColor, intl]);
|
|
43
58
|
const { RichTextMenuComponentProvider, LoadingComponent } = useDashboardComponentsContext();
|
|
44
59
|
const RichTextMenuComponent = useMemo(() => RichTextMenuComponentProvider(widget), [RichTextMenuComponentProvider, widget]);
|
|
45
|
-
useEffect(() => {
|
|
46
|
-
setIsRichTextEditing(isSelected);
|
|
47
|
-
}, [isSelected]);
|
|
48
60
|
useEffect(() => {
|
|
49
61
|
// Deselect widget and commit updated markdown text "on blur"
|
|
50
62
|
if (previousIsSelected && !isSelected && richText !== widget?.content) {
|
|
@@ -56,5 +68,5 @@ export function EditModeDashboardRichText({ widget, clientWidth, clientHeight })
|
|
|
56
68
|
clientWidth > 250 &&
|
|
57
69
|
typeof clientHeight !== "undefined" &&
|
|
58
70
|
clientHeight > 150;
|
|
59
|
-
return (_jsxs(_Fragment, { children: [hasConfigPanelOpen ? (_jsx(RichTextMenuComponent, { widget: widget, isOpen: hasConfigPanelOpen, onClose: closeConfigPanel, items: menuItems })) : null, _jsx(RichText, { referencesEnabled: true, filters: filters, separators: separators, restrictedReferences: restrictedReferences, className: "gd-rich-text-widget", value: richText, onChange: setRichText, renderMode: isRichTextEditing ? "edit" : "view", emptyElement: EmptyElement, LoadingComponent: LoadingComponent, execConfig: execConfig }), isRichTextEditing && showLink ? (_jsx("div", { className: "gd-rich-text-widget-footer", children: _jsx("div", { className: "gd-rich-text-footer-options", children: showLink ? (_jsx("a", { className: "gd-button-link-dimmed gd-icon-circle-question", href: "https://www.gooddata.com/docs/cloud/create-dashboards/rich-text/", rel: "noopener noreferrer", target: "_blank", children: _jsx(FormattedMessage, { id: "richText.formattingOptions" }) })) : null }) })) : null, isConfirmDeleteDialogVisible ? (_jsx(OverlayControllerProvider, { overlayController: overlayController, children: _jsx(ConfirmDialog, { className: "s-rich-text-remove-confirm-dialog", onSubmit: () => dispatch(eagerRemoveSectionItemByWidgetRef(widget.ref)), onCancel: () => setIsConfirmDeleteDialogVisible(false), headline: intl.formatMessage({ id: "richText.deleteDialog.header" }), submitButtonText: intl.formatMessage({ id: "delete" }), cancelButtonText: intl.formatMessage({ id: "cancel" }), children: _jsx(FormattedMessage, { id: "richText.deleteDialog.message" }) }) })) : null] }));
|
|
71
|
+
return (_jsxs(_Fragment, { children: [hasConfigPanelOpen ? (_jsx(RichTextMenuComponent, { widget: widget, isOpen: hasConfigPanelOpen, onClose: closeConfigPanel, items: menuItems })) : null, _jsx(RichText, { referencesEnabled: true, filters: filters, separators: separators, restrictedReferences: restrictedReferences, className: "gd-rich-text-widget", value: richText, onChange: setRichText, renderMode: isRichTextEditing ? "edit" : "view", emptyElement: EmptyElement, LoadingComponent: LoadingComponent, execConfig: execConfig }), isRichTextEditing && showLink ? (_jsx("div", { className: "gd-rich-text-widget-footer", children: _jsx("div", { className: "gd-rich-text-footer-options", children: showLink ? (_jsx("a", { className: "gd-button-link-dimmed gd-icon-circle-question", href: "https://www.gooddata.com/docs/cloud/create-dashboards/rich-text/", rel: "noopener noreferrer", target: "_blank", children: _jsx(FormattedMessage, { id: "richText.formattingOptions" }) })) : null }) })) : null, isAskingToSanitize ? (_jsx(RestrictedReferencesDialog, { value: richText, onSanitized: onSanitized, onCancel: releaseWidget })) : null, isConfirmDeleteDialogVisible ? (_jsx(OverlayControllerProvider, { overlayController: overlayController, children: _jsx(ConfirmDialog, { className: "s-rich-text-remove-confirm-dialog", onSubmit: () => dispatch(eagerRemoveSectionItemByWidgetRef(widget.ref)), onCancel: () => setIsConfirmDeleteDialogVisible(false), headline: intl.formatMessage({ id: "richText.deleteDialog.header" }), submitButtonText: intl.formatMessage({ id: "delete" }), cancelButtonText: intl.formatMessage({ id: "cancel" }), children: _jsx(FormattedMessage, { id: "richText.deleteDialog.message" }) }) })) : null] }));
|
|
60
72
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether the text references an object the current user is not allowed to read. A surface that
|
|
3
|
+
* edits text asks this to know which of its two states it is in, rather than keeping a flag that
|
|
4
|
+
* says the same thing a moment later.
|
|
5
|
+
*/
|
|
6
|
+
export declare function useHasRestrictedReferences(value: string): boolean;
|
|
7
|
+
interface IRestrictedReferencesDialogProps {
|
|
8
|
+
/** The text as it stands. */
|
|
9
|
+
value: string;
|
|
10
|
+
/** Stores the sanitized text. The caller edits it once it no longer holds such a reference. */
|
|
11
|
+
onSanitized: (sanitized: string) => void;
|
|
12
|
+
/** The editor kept the references, and is not editing after all. */
|
|
13
|
+
onCancel: () => void;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Asks before a text that references objects the user cannot read is edited: those references have
|
|
17
|
+
* to go first, and that is destructive. The caller mounts it while it is the answer it is waiting
|
|
18
|
+
* for - confirming rewrites the text, which is what ends the question.
|
|
19
|
+
*/
|
|
20
|
+
export declare function RestrictedReferencesDialog({ value, onSanitized, onCancel, }: IRestrictedReferencesDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=RestrictedReferencesDialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RestrictedReferencesDialog.d.ts","sourceRoot":"","sources":["../../../../src/presentation/widget/richText/RestrictedReferencesDialog.tsx"],"names":[],"mappings":"AAqBA;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAWjE;AAED,UAAU,gCAAgC;IACtC,6BAA6B;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,+FAA+F;IAC/F,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,oEAAoE;IACpE,QAAQ,EAAE,MAAM,IAAI,CAAC;CACxB;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,EACvC,KAAK,EACL,WAAW,EACX,QAAQ,GACX,EAAE,gCAAgC,2CAyBlC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// (C) 2026 GoodData Corporation
|
|
3
|
+
import { useCallback, useMemo } from "react";
|
|
4
|
+
import { useIntl } from "react-intl";
|
|
5
|
+
import { areObjRefsEqual } from "@gooddata/sdk-model";
|
|
6
|
+
import { UiConfirmDialog, collectReferences, replaceReferences } from "@gooddata/sdk-ui-kit";
|
|
7
|
+
import { useDashboardSelector } from "../../../model/react/DashboardStoreProvider.js";
|
|
8
|
+
import { selectRestrictedRichTextReferences } from "../../../model/store/unavailableObjects/unavailableObjectsSelectors.js";
|
|
9
|
+
/**
|
|
10
|
+
* Put in the text in place of a reference the editor may not read, as the design has it. It is
|
|
11
|
+
* stored as the text itself, and thus is the same for every reader and not localised.
|
|
12
|
+
*/
|
|
13
|
+
const RESTRICTED_REFERENCE_REPLACEMENT = "???";
|
|
14
|
+
/** The wrapper is there to catch events, and must not take part in the layout it is dropped into. */
|
|
15
|
+
const noBoxOfItsOwn = { display: "contents" };
|
|
16
|
+
/**
|
|
17
|
+
* Whether the text references an object the current user is not allowed to read. A surface that
|
|
18
|
+
* edits text asks this to know which of its two states it is in, rather than keeping a flag that
|
|
19
|
+
* says the same thing a moment later.
|
|
20
|
+
*/
|
|
21
|
+
export function useHasRestrictedReferences(value) {
|
|
22
|
+
const restrictedReferences = useDashboardSelector(selectRestrictedRichTextReferences);
|
|
23
|
+
return useMemo(() => {
|
|
24
|
+
if (restrictedReferences.length === 0) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
return Object.values(collectReferences(value)).some((reference) => restrictedReferences.some((restricted) => areObjRefsEqual(restricted, reference.ref)));
|
|
28
|
+
}, [value, restrictedReferences]);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Asks before a text that references objects the user cannot read is edited: those references have
|
|
32
|
+
* to go first, and that is destructive. The caller mounts it while it is the answer it is waiting
|
|
33
|
+
* for - confirming rewrites the text, which is what ends the question.
|
|
34
|
+
*/
|
|
35
|
+
export function RestrictedReferencesDialog({ value, onSanitized, onCancel, }) {
|
|
36
|
+
const intl = useIntl();
|
|
37
|
+
const restrictedReferences = useDashboardSelector(selectRestrictedRichTextReferences);
|
|
38
|
+
const onConfirm = useCallback(() => {
|
|
39
|
+
onSanitized(replaceReferences(value, restrictedReferences, RESTRICTED_REFERENCE_REPLACEMENT));
|
|
40
|
+
}, [value, restrictedReferences, onSanitized]);
|
|
41
|
+
return (_jsx("span", { style: noBoxOfItsOwn, onClick: (event) => event.stopPropagation(), children: _jsx(UiConfirmDialog, { isOpen: true, dataTestId: "rich-text-sanitize-references-dialog", title: intl.formatMessage({ id: "richText.restrictedReferences.dialog.header" }), description: intl.formatMessage({ id: "richText.restrictedReferences.dialog.message" }), confirmLabel: intl.formatMessage({ id: "richText.restrictedReferences.dialog.submit" }), confirmVariant: "danger", onConfirm: onConfirm, onCancel: onCancel, onClose: onCancel }) }));
|
|
42
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useEditableRichTextMenu.d.ts","sourceRoot":"","sources":["../../../../src/presentation/widget/richText/useEditableRichTextMenu.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"useEditableRichTextMenu.d.ts","sourceRoot":"","sources":["../../../../src/presentation/widget/richText/useEditableRichTextMenu.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAQ3D,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,KAAK,6BAA6B,GAAG;IACjC,MAAM,EAAE,eAAe,CAAC;IACxB,SAAS,EAAE,MAAM,IAAI,CAAC;CACzB,CAAC;AAEF,eAAO,MAAM,uBAAuB,WACxB,6BAA6B,KACtC;IAAE,SAAS,EAAE,iBAAiB,EAAE,CAAA;CA8BlC,CAAC"}
|
|
@@ -3,6 +3,7 @@ import { useMemo } from "react";
|
|
|
3
3
|
import { useIntl } from "react-intl";
|
|
4
4
|
import { useDashboardDispatch, useDashboardSelector } from "../../../model/react/DashboardStoreProvider.js";
|
|
5
5
|
import { useDashboardEventDispatch } from "../../../model/react/useDashboardEventDispatch.js";
|
|
6
|
+
import { useIsWidgetRestricted } from "../../../model/react/useIsWidgetRestricted.js";
|
|
6
7
|
import { selectEnableRichTextWidgetFilterConfiguration } from "../../../model/store/config/configSelectors.js";
|
|
7
8
|
import { useDashboardCustomizationsContext } from "../../dashboardContexts/DashboardCustomizationsContext.js";
|
|
8
9
|
import { getDefaultRichTextEditMode } from "../richTextMenu/DefaultDashboardRichTextMenu/getDefaultRichTextEditMenuItems.js";
|
|
@@ -12,15 +13,18 @@ export const useEditableRichTextMenu = (config) => {
|
|
|
12
13
|
const dispatch = useDashboardDispatch();
|
|
13
14
|
const eventDispatch = useDashboardEventDispatch();
|
|
14
15
|
const enableRichTextWidgetFilterConfiguration = useDashboardSelector(selectEnableRichTextWidgetFilterConfiguration);
|
|
16
|
+
// there is nothing to configure about a reference the editor is not allowed to read, so the
|
|
17
|
+
// menu of such a widget holds removal alone
|
|
18
|
+
const isRestricted = useIsWidgetRestricted(widget);
|
|
15
19
|
const { richTextMenuItemsProvider } = useDashboardCustomizationsContext();
|
|
16
20
|
const defaultMenuItems = useMemo(() => {
|
|
17
21
|
return getDefaultRichTextEditMode(widget, {
|
|
18
22
|
intl,
|
|
19
23
|
dispatch,
|
|
20
24
|
eventDispatch,
|
|
21
|
-
enableRichTextWidgetFilterConfiguration,
|
|
25
|
+
enableRichTextWidgetFilterConfiguration: enableRichTextWidgetFilterConfiguration && !isRestricted,
|
|
22
26
|
});
|
|
23
|
-
}, [dispatch, eventDispatch, intl, widget, enableRichTextWidgetFilterConfiguration]);
|
|
27
|
+
}, [dispatch, eventDispatch, intl, widget, enableRichTextWidgetFilterConfiguration, isRestricted]);
|
|
24
28
|
const menuItems = useMemo(() => {
|
|
25
29
|
return richTextMenuItemsProvider
|
|
26
30
|
? richTextMenuItemsProvider(widget, defaultMenuItems, closeMenu, "edit")
|