@gooddata/sdk-ui-catalog 11.43.0-alpha.3 → 11.43.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/catalogDetail/CatalogDetailActions.d.ts +1 -3
- package/esm/catalogDetail/CatalogDetailActions.d.ts.map +1 -1
- package/esm/catalogDetail/CatalogDetailActions.js +6 -2
- package/esm/catalogDetail/CatalogDetailContent.d.ts.map +1 -1
- package/esm/catalogDetail/CatalogDetailContent.js +33 -54
- package/esm/catalogDetail/CatalogDetailTabMetadata.d.ts +5 -10
- package/esm/catalogDetail/CatalogDetailTabMetadata.d.ts.map +1 -1
- package/esm/catalogDetail/CatalogDetailTabMetadata.js +3 -7
- package/esm/catalogDetail/hooks/useCatalogItemLoad.d.ts.map +1 -1
- package/esm/catalogDetail/hooks/useCatalogItemLoad.js +8 -2
- package/esm/catalogDetail/share/CatalogItemAccessRow.d.ts +10 -0
- package/esm/catalogDetail/share/CatalogItemAccessRow.d.ts.map +1 -0
- package/esm/catalogDetail/share/CatalogItemAccessRow.js +29 -0
- package/esm/catalogDetail/share/CatalogItemShareDialog.d.ts +10 -0
- package/esm/catalogDetail/share/CatalogItemShareDialog.d.ts.map +1 -0
- package/esm/catalogDetail/share/CatalogItemShareDialog.js +29 -0
- package/esm/catalogDetail/share/CatalogItemShareDialogInner.d.ts +10 -0
- package/esm/catalogDetail/share/CatalogItemShareDialogInner.d.ts.map +1 -0
- package/esm/catalogDetail/share/CatalogItemShareDialogInner.js +20 -0
- package/esm/catalogDetail/share/CatalogItemShareProvider.d.ts +71 -0
- package/esm/catalogDetail/share/CatalogItemShareProvider.d.ts.map +1 -0
- package/esm/catalogDetail/share/CatalogItemShareProvider.js +83 -0
- package/package.json +14 -14
|
@@ -11,11 +11,9 @@ export interface ICatalogDetailActionsProps {
|
|
|
11
11
|
onCatalogItemCreate?: (item: ICatalogItem) => void;
|
|
12
12
|
onCatalogItemUpdate?: (item: ICatalogItem) => void;
|
|
13
13
|
onCatalogItemDelete?: (ref: ICatalogItemRef) => void;
|
|
14
|
-
/** When set, a Share button is shown next to Open. Undefined hides it (not shareable / flag off). */
|
|
15
|
-
onShareClick?: () => void;
|
|
16
14
|
}
|
|
17
15
|
/**
|
|
18
16
|
* @internal
|
|
19
17
|
*/
|
|
20
|
-
export declare function CatalogDetailActions({ item, canEdit, onOpen, onCatalogItemCreate, onCatalogItemUpdate, onCatalogItemDelete
|
|
18
|
+
export declare function CatalogDetailActions({ item, canEdit, onOpen, onCatalogItemCreate, onCatalogItemUpdate, onCatalogItemDelete }: ICatalogDetailActionsProps): import("react/jsx-runtime").JSX.Element | null;
|
|
21
19
|
//# sourceMappingURL=CatalogDetailActions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CatalogDetailActions.d.ts","sourceRoot":"","sources":["../../src/catalogDetail/CatalogDetailActions.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,UAAU,EAAkC,MAAM,OAAO,CAAC;AAUxE,OAAO,KAAK,EAAE,YAAY,EAAyB,eAAe,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"CatalogDetailActions.d.ts","sourceRoot":"","sources":["../../src/catalogDetail/CatalogDetailActions.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,UAAU,EAAkC,MAAM,OAAO,CAAC;AAUxE,OAAO,KAAK,EAAE,YAAY,EAAyB,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAQpG,OAAO,KAAK,EAAwB,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAczE;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACvC,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClE,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACnD,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACnD,mBAAmB,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,IAAI,CAAC;CACxD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,EACjC,IAAI,EACJ,OAAO,EACP,MAAM,EACN,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACtB,EAAE,0BAA0B,kDA8I5B"}
|
|
@@ -10,6 +10,7 @@ import { ParameterCreateDialog } from "../parameter/ParameterCreateDialog.js";
|
|
|
10
10
|
import { ParameterDeleteDialog } from "../parameter/ParameterDeleteDialog.js";
|
|
11
11
|
import { ParameterEditDialog } from "../parameter/ParameterEditDialog.js";
|
|
12
12
|
import { CatalogDetailActionBar } from "./CatalogDetailActionBar.js";
|
|
13
|
+
import { useCatalogItemShareActions } from "./share/CatalogItemShareProvider.js";
|
|
13
14
|
import { ShareButton } from "./share/ShareButton.js";
|
|
14
15
|
const messages = defineMessages({
|
|
15
16
|
parameterDuplicate: { id: "analyticsCatalog.parameter.dialog.edit.duplicate" },
|
|
@@ -18,10 +19,13 @@ const messages = defineMessages({
|
|
|
18
19
|
/**
|
|
19
20
|
* @internal
|
|
20
21
|
*/
|
|
21
|
-
export function CatalogDetailActions({ item, canEdit, onOpen, onCatalogItemCreate, onCatalogItemUpdate, onCatalogItemDelete,
|
|
22
|
+
export function CatalogDetailActions({ item, canEdit, onOpen, onCatalogItemCreate, onCatalogItemUpdate, onCatalogItemDelete, }) {
|
|
22
23
|
const intl = useIntl();
|
|
23
24
|
const workspaceId = useWorkspaceStrict();
|
|
24
25
|
const [dialog, setDialog] = useState(undefined);
|
|
26
|
+
// The Share button reads the share context directly (actions only), so it never
|
|
27
|
+
// re-renders as access is edited and is shown only when sharing is available.
|
|
28
|
+
const share = useCatalogItemShareActions();
|
|
25
29
|
const parameterActions = useMemo(() => [
|
|
26
30
|
{
|
|
27
31
|
id: "duplicate",
|
|
@@ -83,7 +87,7 @@ export function CatalogDetailActions({ item, canEdit, onOpen, onCatalogItemCreat
|
|
|
83
87
|
return (_jsxs(_Fragment, { children: [
|
|
84
88
|
_jsx(CatalogDetailActionBar, { item: item, workspaceId: workspaceId, actions: parameterActions, onEditClick: handleEditOpen, onActionsMenuSelect: handleActionsMenuSelect }), dialog?.kind === "edit" ? (_jsx(ParameterEditDialog, { item: dialog.item, onClose: closeDialog, onSaved: handleSaved, onDuplicate: handleEditDuplicate })) : null, dialog?.kind === "duplicate" ? (_jsx(ParameterCreateDialog, { sourceItem: dialog.item, onClose: closeDialog, onCreated: handleCreated })) : null, dialog?.kind === "delete" ? (_jsx(ParameterDeleteDialog, { item: dialog.item, onClose: closeDialog, onDeleted: handleDeleted })) : null] }));
|
|
85
89
|
}
|
|
86
|
-
return (_jsxs("div", { className: "gd-analytics-catalog-detail__header-actions", children: [
|
|
90
|
+
return (_jsxs("div", { className: "gd-analytics-catalog-detail__header-actions", children: [share.active ? _jsx(ShareButton, { onClick: share.open }) : null, _jsx(UiButton, { label: intl.formatMessage({ id: "analyticsCatalog.catalogItem.open" }), variant: "primary", accessibilityConfig: { role: "link" }, onClick: (event) => {
|
|
87
91
|
onOpen?.(event, {
|
|
88
92
|
item,
|
|
89
93
|
workspaceId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CatalogDetailContent.d.ts","sourceRoot":"","sources":["../../src/catalogDetail/CatalogDetailContent.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,UAAU,
|
|
1
|
+
{"version":3,"file":"CatalogDetailContent.d.ts","sourceRoot":"","sources":["../../src/catalogDetail/CatalogDetailContent.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,UAAU,EAAgC,MAAM,OAAO,CAAC;AAUtE,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAGlF,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAkBzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AASnD;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACvC;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB;;OAEG;IACH,UAAU,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC/B;;OAEG;IACH,gBAAgB,CAAC,EAAE,eAAe,GAAG,YAAY,GAAG,IAAI,CAAC;IACzD;;OAEG;IACH,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC5E;;OAEG;IACH,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC;;OAEG;IACH,uBAAuB,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,EAAE,eAAe,KAAK,IAAI,CAAC;IAC5E;;OAEG;IACH,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACnD;;OAEG;IACH,mBAAmB,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,CAAC;IACnD;;OAEG;IACH,mBAAmB,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,IAAI,CAAC;IACrD;;OAEG;IACH,wBAAwB,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CACrD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,EACjC,QAAQ,EACR,UAAU,EACV,gBAAgB,EAChB,WAAW,EACX,UAAU,EACV,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,wBAAwB,EACxB,uBAAuB,EAC1B,EAAE,0BAA0B,2CAoN5B"}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
// (C) 2025-2026 GoodData Corporation
|
|
3
|
-
import { useCallback, useMemo, useRef
|
|
3
|
+
import { useCallback, useMemo, useRef } from "react";
|
|
4
4
|
import cx from "classnames";
|
|
5
5
|
import { useIntl } from "react-intl";
|
|
6
6
|
import {} from "@gooddata/sdk-model";
|
|
7
7
|
import { useLocalStorage } from "@gooddata/sdk-ui";
|
|
8
|
-
import { ObjectShareDialog, useObjectShare } from "@gooddata/sdk-ui-ext";
|
|
9
8
|
import { UiSkeleton, UiTabs } from "@gooddata/sdk-ui-kit";
|
|
10
9
|
import { canEditCatalogItem } from "../catalogItem/permission.js";
|
|
11
10
|
import {} from "../catalogItem/types.js";
|
|
@@ -23,6 +22,9 @@ import { CatalogDetailTabLineage } from "./CatalogDetailTabLineage.js";
|
|
|
23
22
|
import { CatalogDetailTabMetadata } from "./CatalogDetailTabMetadata.js";
|
|
24
23
|
import { CatalogDetailTabQuality } from "./CatalogDetailTabQuality.js";
|
|
25
24
|
import { useCatalogItemUpdate } from "./hooks/useCatalogItemUpdate.js";
|
|
25
|
+
import { CatalogItemAccessRow } from "./share/CatalogItemAccessRow.js";
|
|
26
|
+
import { CatalogItemShareDialog } from "./share/CatalogItemShareDialog.js";
|
|
27
|
+
import { CatalogItemShareProvider } from "./share/CatalogItemShareProvider.js";
|
|
26
28
|
import { isShareableCatalogItem, toShareTarget } from "./share/guards.js";
|
|
27
29
|
import { useShareableLabels } from "./share/useShareableLabels.js";
|
|
28
30
|
const Tabs = {
|
|
@@ -52,33 +54,9 @@ export function CatalogDetailContent({ objectId, objectType, objectDefinition, o
|
|
|
52
54
|
const enableColumnLevelPermissions = Boolean(settings?.enableColumnLevelPermissions);
|
|
53
55
|
const shareableItem = item && enableColumnLevelPermissions && isShareableCatalogItem(item) ? item : undefined;
|
|
54
56
|
const shareTarget = useMemo(() => (shareableItem ? toShareTarget(shareableItem) : undefined), [shareableItem]);
|
|
55
|
-
// Labels of the shared attribute drive the per-grantee
|
|
57
|
+
// Labels of the shared attribute drive both the header info row and the per-grantee
|
|
58
|
+
// label-scope picker; fetched once here and passed to the share provider.
|
|
56
59
|
const shareLabels = useShareableLabels(shareableItem);
|
|
57
|
-
// One controller drives both the dialog and the inline access row; the row
|
|
58
|
-
// reads `state.summary`, so a save inside the dialog refreshes it too.
|
|
59
|
-
// `labelsLoading`/`labelsError` keep the controller label-unresolved while labels
|
|
60
|
-
// are still pending, disabling every access-changing control (a write would
|
|
61
|
-
// otherwise diff against an empty label set and orphan real per-label grants).
|
|
62
|
-
const shareController = useObjectShare(shareTarget, {
|
|
63
|
-
labels: shareLabels.labels,
|
|
64
|
-
labelsError: shareLabels.error,
|
|
65
|
-
labelsLoading: shareLabels.loading,
|
|
66
|
-
});
|
|
67
|
-
const [isShareOpen, setIsShareOpen] = useState(false);
|
|
68
|
-
// The detail view is reused as the user navigates between objects, so reset the
|
|
69
|
-
// open dialog when the shareable target changes (or becomes non-shareable) —
|
|
70
|
-
// otherwise it would linger open on the next object. Reset during render (React's
|
|
71
|
-
// "adjust state on prop change" idiom) rather than in an effect.
|
|
72
|
-
const shareTargetKey = shareableItem?.identifier;
|
|
73
|
-
const [openForKey, setOpenForKey] = useState(undefined);
|
|
74
|
-
if (isShareOpen && openForKey !== shareTargetKey) {
|
|
75
|
-
setIsShareOpen(false);
|
|
76
|
-
}
|
|
77
|
-
const openShare = useCallback(() => {
|
|
78
|
-
setOpenForKey(shareTargetKey);
|
|
79
|
-
setIsShareOpen(true);
|
|
80
|
-
}, [shareTargetKey]);
|
|
81
|
-
const closeShare = useCallback(() => setIsShareOpen(false), []);
|
|
82
60
|
const separators = settings?.separators;
|
|
83
61
|
const enableMetricFormatOverrides = Boolean(settings?.["enableMetricFormatOverrides"]);
|
|
84
62
|
const currencyFormatOverride = settings?.currencyFormatOverride ?? null;
|
|
@@ -132,32 +110,33 @@ export function CatalogDetailContent({ objectId, objectType, objectDefinition, o
|
|
|
132
110
|
return tabs;
|
|
133
111
|
}, [intl, isCertificationVisible, isQualityVisible, isLineageVisible, issueCount]);
|
|
134
112
|
const [selectedTabId, setSelectedTabId] = useSelectedTabId(tabs);
|
|
135
|
-
return (_jsxs("div", { className: "gd-analytics-catalog-detail", children: [
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
113
|
+
return (_jsx(CatalogItemShareProvider, { shareableItem: shareableItem, target: shareTarget, labels: shareLabels, children: _jsxs("div", { className: "gd-analytics-catalog-detail", children: [
|
|
114
|
+
_jsx(CatalogDetailStatus, { status: status, error: error, children: item ? (_jsxs("div", { className: cx("gd-analytics-catalog-detail__content", {
|
|
115
|
+
lineage: selectedTabId === Tabs.LINEAGE,
|
|
116
|
+
}), children: [
|
|
117
|
+
_jsx(CatalogDetailHeader, { item: item, canEdit: canEdit, updateItemTitle: updateItemTitle, updateItemDescription: updateItemDescription, isDescriptionGenerationEnabled: isDescriptionGenerationEnabled, headerRef: headerRef, labels: shareableItem ? shareLabels.labels : undefined, labelsLoading: shareableItem ? shareLabels.loading : false, showInfoRow: enableColumnLevelPermissions, actions: _jsx(CatalogDetailActions, { item: item, canEdit: canEdit, onOpen: onOpenClick, onCatalogItemCreate: onCatalogItemCreate, onCatalogItemUpdate: applyItemUpdate, onCatalogItemDelete: applyItemDelete }) }), _jsx(UiTabs, { size: "large", tabs: tabs, onTabSelect: (tab) => setSelectedTabId(tab.id), selectedTabId: selectedTabId }), selectedTabId === Tabs.METADATA && (_jsx(CatalogDetailTabMetadata, { item: item, canEdit: canEdit,
|
|
118
|
+
// With OLP off the dataset isn't relocated to the header,
|
|
119
|
+
// so keep the Dataset row in the metadata tab.
|
|
120
|
+
showDatasetRow: !enableColumnLevelPermissions, onTagClick: (tag) => {
|
|
121
|
+
onTagClick?.(tag.label);
|
|
122
|
+
}, onTagAdd: (tag) => {
|
|
123
|
+
// Adding unique tags only
|
|
124
|
+
updateItemTags([...new Set([...item.tags, tag.label])]);
|
|
125
|
+
}, onTagRemove: (tag) => {
|
|
126
|
+
updateItemTags(item.tags.filter((t) => t !== tag.label));
|
|
127
|
+
}, onIsHiddenChange: (isHidden) => {
|
|
128
|
+
updateItemIsHidden(isHidden);
|
|
129
|
+
}, onIsHiddenFromKdaChange: (isHiddenFromKda) => {
|
|
130
|
+
updateItemIsHiddenFromKda(isHiddenFromKda);
|
|
131
|
+
}, onMetricTypeChange: (metricType) => {
|
|
132
|
+
updateItemMetricType(metricType);
|
|
133
|
+
}, onFormatChange: (format) => {
|
|
134
|
+
updateItemFormat(format);
|
|
135
|
+
}, separators: separators, currencyFormatOverride: enableMetricFormatOverrides ? currencyFormatOverride : undefined, enableMetricFormatOverrides: enableMetricFormatOverrides, accessRow: _jsx(CatalogItemAccessRow, {}) })), selectedTabId === Tabs.CERTIFICATION && isCertificationVisible ? (_jsx(CatalogDetailTabCertification, { item: item, canEdit: canEdit, onCertificationChange: (certification) => {
|
|
136
|
+
updateItemCertification(certification);
|
|
137
|
+
} })) : null, selectedTabId === Tabs.QUALITY &&
|
|
138
|
+
(isQualityLoading ? (_jsx(UiSkeleton, { itemsCount: 2, itemHeight: 65, itemsGap: 10 })) : (_jsx(CatalogDetailTabQuality, { item: item, issues: issues, canEdit: canEdit, onEditClick: handleEditClick, onCatalogItemNavigation: onCatalogItemNavigation }))), selectedTabId === Tabs.LINEAGE && _jsx(CatalogDetailTabLineage, { item: item })] })) : null }), _jsx(CatalogItemShareDialog, {})
|
|
139
|
+
] }) }));
|
|
161
140
|
}
|
|
162
141
|
/**
|
|
163
142
|
* Hook for managing the currently selected tab ID with persistent storage.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
1
2
|
import type { ISeparators, MetricType } from "@gooddata/sdk-model";
|
|
2
|
-
import type { IObjectAccessSummary } from "@gooddata/sdk-ui-ext";
|
|
3
3
|
import { type IUiTagDef } from "@gooddata/sdk-ui-kit";
|
|
4
4
|
import { type ICatalogItem } from "../catalogItem/types.js";
|
|
5
5
|
type Props = {
|
|
@@ -15,22 +15,17 @@ type Props = {
|
|
|
15
15
|
separators?: ISeparators;
|
|
16
16
|
currencyFormatOverride?: string | null;
|
|
17
17
|
enableMetricFormatOverrides?: boolean;
|
|
18
|
-
/** Access summary for the inline row. Undefined when sharing is unavailable for this item. */
|
|
19
|
-
accessSummary?: IObjectAccessSummary;
|
|
20
18
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* the row must stay because Share is still reachable for the object.
|
|
19
|
+
* Inline access row rendered at the top of the list. Self-contained (reads the
|
|
20
|
+
* share context); undefined / nothing rendered when sharing is unavailable.
|
|
24
21
|
*/
|
|
25
|
-
|
|
26
|
-
/** Opens the share dialog from the access row. Required for the row to render. */
|
|
27
|
-
onAccessOpen?: () => void;
|
|
22
|
+
accessRow?: ReactNode;
|
|
28
23
|
/**
|
|
29
24
|
* Show the Dataset row here. True when object-level permissions are off — with
|
|
30
25
|
* the flag on the dataset is relocated to the header info row instead.
|
|
31
26
|
*/
|
|
32
27
|
showDatasetRow?: boolean;
|
|
33
28
|
};
|
|
34
|
-
export declare function CatalogDetailTabMetadata({ item, canEdit, onTagClick, onTagAdd, onTagRemove, onIsHiddenChange, onIsHiddenFromKdaChange, onMetricTypeChange, onFormatChange, separators, currencyFormatOverride, enableMetricFormatOverrides,
|
|
29
|
+
export declare function CatalogDetailTabMetadata({ item, canEdit, onTagClick, onTagAdd, onTagRemove, onIsHiddenChange, onIsHiddenFromKdaChange, onMetricTypeChange, onFormatChange, separators, currencyFormatOverride, enableMetricFormatOverrides, accessRow, showDatasetRow }: Props): import("react/jsx-runtime").JSX.Element;
|
|
35
30
|
export {};
|
|
36
31
|
//# sourceMappingURL=CatalogDetailTabMetadata.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CatalogDetailTabMetadata.d.ts","sourceRoot":"","sources":["../../src/catalogDetail/CatalogDetailTabMetadata.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CatalogDetailTabMetadata.d.ts","sourceRoot":"","sources":["../../src/catalogDetail/CatalogDetailTabMetadata.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,EAAE,KAAK,SAAS,EAA6B,MAAM,sBAAsB,CAAC;AASjF,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAO5D,KAAK,KAAK,GAAG;IACT,IAAI,EAAE,YAAY,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,CAAC,GAAG,EAAE,SAAS,KAAK,IAAI,CAAC;IACrC,QAAQ,EAAE,CAAC,GAAG,EAAE,SAAS,KAAK,IAAI,CAAC;IACnC,WAAW,EAAE,CAAC,GAAG,EAAE,SAAS,KAAK,IAAI,CAAC;IACtC,gBAAgB,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9C,uBAAuB,EAAE,CAAC,eAAe,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5D,kBAAkB,CAAC,EAAE,CAAC,UAAU,EAAE,UAAU,GAAG,SAAS,KAAK,IAAI,CAAC;IAClE,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACjD,UAAU,CAAC,EAAE,WAAW,CAAC;IACzB,sBAAsB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvC,2BAA2B,CAAC,EAAE,OAAO,CAAC;IACtC;;;OAGG;IACH,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF,wBAAgB,wBAAwB,CAAC,EACrC,IAAI,EACJ,OAAO,EACP,UAAU,EACV,QAAQ,EACR,WAAW,EACX,gBAAgB,EAChB,uBAAuB,EACvB,kBAAkB,EAClB,cAAc,EACd,UAAU,EACV,sBAAsB,EACtB,2BAA2B,EAC3B,SAAS,EACT,cAAc,EACjB,EAAE,KAAK,2CAiJP"}
|
|
@@ -2,24 +2,20 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
// (C) 2025-2026 GoodData Corporation
|
|
3
3
|
import cx from "classnames";
|
|
4
4
|
import { FormattedMessage, useIntl } from "react-intl";
|
|
5
|
-
import { UiDate, UiIcon,
|
|
5
|
+
import { UiDate, UiIcon, UiTooltip } from "@gooddata/sdk-ui-kit";
|
|
6
6
|
import { isCatalogItemAttribute, isCatalogItemDataSet, isCatalogItemFact, isCatalogItemHidable, isCatalogItemMeasure, } from "../catalogItem/guards.js";
|
|
7
7
|
import {} from "../catalogItem/types.js";
|
|
8
8
|
import { CatalogDetailContentRow } from "./CatalogDetailContentRow.js";
|
|
9
9
|
import { CatalogDetailGranularities } from "./CatalogDetailGranularities.js";
|
|
10
10
|
import { CatalogDetailMetricSettings } from "./CatalogDetailMetricSettings.js";
|
|
11
11
|
import { CatalogDetailTags } from "./CatalogDetailTags.js";
|
|
12
|
-
|
|
13
|
-
export function CatalogDetailTabMetadata({ item, canEdit, onTagClick, onTagAdd, onTagRemove, onIsHiddenChange, onIsHiddenFromKdaChange, onMetricTypeChange, onFormatChange, separators, currencyFormatOverride, enableMetricFormatOverrides, accessSummary, accessUnavailable, onAccessOpen, showDatasetRow, }) {
|
|
12
|
+
export function CatalogDetailTabMetadata({ item, canEdit, onTagClick, onTagAdd, onTagRemove, onIsHiddenChange, onIsHiddenFromKdaChange, onMetricTypeChange, onFormatChange, separators, currencyFormatOverride, enableMetricFormatOverrides, accessRow, showDatasetRow, }) {
|
|
14
13
|
const intl = useIntl();
|
|
15
14
|
const isMeasure = isCatalogItemMeasure(item);
|
|
16
15
|
const isDataSet = isCatalogItemDataSet(item);
|
|
17
16
|
const granularities = isDataSet ? (item.dataSet.attributes ?? []) : [];
|
|
18
17
|
const datasetTitle = isCatalogItemAttribute(item) || isCatalogItemFact(item) ? item.dataSet?.title : undefined;
|
|
19
|
-
return (_jsxs("dl", { className: "gd-analytics-catalog-detail__tab-content", children: [
|
|
20
|
-
// Summary not available yet (loading or load error). Keep the row —
|
|
21
|
-
// Share is still reachable — with a placeholder rather than omitting it.
|
|
22
|
-
_jsx(CatalogDetailContentRow, { title: _jsx(FormattedMessage, { id: "analyticsCatalog.share.access.row.label" }), content: _jsx(UiSkeleton, { itemWidth: 200, itemHeight: 20 }) })) : null, showDatasetRow && datasetTitle ? (_jsx(CatalogDetailContentRow, { title: _jsx(FormattedMessage, { id: "analyticsCatalog.column.title.dataSet" }), content: datasetTitle })) : null, isDataSet && granularities.length > 0 ? (_jsx(CatalogDetailContentRow, { title: _jsx(FormattedMessage, { id: "analyticsCatalog.column.title.granularities" }), content: _jsx(CatalogDetailGranularities, { granularities: granularities }) })) : null, _jsx(CatalogDetailContentRow, { title: _jsx(FormattedMessage, { id: "analyticsCatalog.column.title.createdBy" }), content: item.createdBy ?? undefined }), _jsx(CatalogDetailContentRow, { title: _jsx(FormattedMessage, { id: "analyticsCatalog.column.title.createdAt" }), content: item.createdAt ? _jsx(UiDate, { date: item.createdAt, locale: intl.locale }) : undefined }), item.createdBy === item.updatedBy && !item.updatedBy ? null : (_jsx(CatalogDetailContentRow, { title: _jsx(FormattedMessage, { id: "analyticsCatalog.column.title.updatedBy" }), content: item.updatedBy ?? undefined })), item.createdAt?.getTime() === item.updatedAt?.getTime() ? null : (_jsx(CatalogDetailContentRow, { title: _jsx(FormattedMessage, { id: "analyticsCatalog.column.title.updatedAt" }), content: item.updatedAt ? _jsx(UiDate, { date: item.updatedAt, locale: intl.locale }) : undefined })), isCatalogItemHidable(item) ? (_jsx(CatalogDetailContentRow, { title: _jsxs(_Fragment, { children: [
|
|
18
|
+
return (_jsxs("dl", { className: "gd-analytics-catalog-detail__tab-content", children: [accessRow, showDatasetRow && datasetTitle ? (_jsx(CatalogDetailContentRow, { title: _jsx(FormattedMessage, { id: "analyticsCatalog.column.title.dataSet" }), content: datasetTitle })) : null, isDataSet && granularities.length > 0 ? (_jsx(CatalogDetailContentRow, { title: _jsx(FormattedMessage, { id: "analyticsCatalog.column.title.granularities" }), content: _jsx(CatalogDetailGranularities, { granularities: granularities }) })) : null, _jsx(CatalogDetailContentRow, { title: _jsx(FormattedMessage, { id: "analyticsCatalog.column.title.createdBy" }), content: item.createdBy ?? undefined }), _jsx(CatalogDetailContentRow, { title: _jsx(FormattedMessage, { id: "analyticsCatalog.column.title.createdAt" }), content: item.createdAt ? _jsx(UiDate, { date: item.createdAt, locale: intl.locale }) : undefined }), item.createdBy === item.updatedBy && !item.updatedBy ? null : (_jsx(CatalogDetailContentRow, { title: _jsx(FormattedMessage, { id: "analyticsCatalog.column.title.updatedBy" }), content: item.updatedBy ?? undefined })), item.createdAt?.getTime() === item.updatedAt?.getTime() ? null : (_jsx(CatalogDetailContentRow, { title: _jsx(FormattedMessage, { id: "analyticsCatalog.column.title.updatedAt" }), content: item.updatedAt ? _jsx(UiDate, { date: item.updatedAt, locale: intl.locale }) : undefined })), isCatalogItemHidable(item) ? (_jsx(CatalogDetailContentRow, { title: _jsxs(_Fragment, { children: [
|
|
23
19
|
_jsx(FormattedMessage, { id: "analyticsCatalog.column.title.isHidden" }), _jsx(UiTooltip, { anchor: _jsx(UiIcon, { type: "question", size: 12, color: "complementary-6" }), content: _jsx(FormattedMessage, { id: "analyticsCatalog.column.isHidden.field.tooltip" }), arrowPlacement: "left", optimalPlacement: true, offset: 10, width: 280, triggerBy: ["hover", "click"] })
|
|
24
20
|
] }), content: _jsxs("label", { className: "input-checkbox-toggle", children: [
|
|
25
21
|
_jsx("input", { type: "checkbox", checked: item.isHidden !== true, disabled: !canEdit, "aria-label": intl.formatMessage({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCatalogItemLoad.d.ts","sourceRoot":"","sources":["../../../src/catalogDetail/hooks/useCatalogItemLoad.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useCatalogItemLoad.d.ts","sourceRoot":"","sources":["../../../src/catalogDetail/hooks/useCatalogItemLoad.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,4BAA4B,CAAC;AACrF,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAE5D,MAAM,WAAW,mBAAmB;IAChC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAC/B,gBAAgB,CAAC,EAAE,eAAe,GAAG,YAAY,GAAG,IAAI,CAAC;CAC5D;AAED,wBAAgB,kBAAkB,CAAC,EAAE,gBAAgB,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,mBAAmB,GAAG;IACjG,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;IACpD,IAAI,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAC3B,KAAK,CAAC,EAAE,KAAK,CAAC;CACjB,CAmDA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// (C) 2025-2026 GoodData Corporation
|
|
2
|
-
import { useBackendStrict, useCancelablePromise, useWorkspaceStrict } from "@gooddata/sdk-ui";
|
|
2
|
+
import { convertError, isNotFound, useBackendStrict, useCancelablePromise, useWorkspaceStrict, } from "@gooddata/sdk-ui";
|
|
3
3
|
import { convertAttributeToCatalogItem, convertDashboardToCatalogItem, convertDataSetToCatalogItem, convertFactToCatalogItem, convertInsightToCatalogItem, convertMeasureToCatalogItem, convertParameterToCatalogItem, } from "../../catalogItem/converter.js";
|
|
4
4
|
import { isCatalogItemLoaded } from "../../catalogItem/guards.js";
|
|
5
5
|
import {} from "../../catalogItem/types.js";
|
|
@@ -20,7 +20,13 @@ export function useCatalogItemLoad({ objectDefinition, objectId, objectType }) {
|
|
|
20
20
|
if (filled) {
|
|
21
21
|
return Promise.resolve(objectDefinition);
|
|
22
22
|
}
|
|
23
|
-
|
|
23
|
+
// A 404 must reach CatalogDetailStatus as a NotFoundSdkError so isNotFound() shows
|
|
24
|
+
// the not-found page; convertError maps it. Other errors are rethrown untouched so
|
|
25
|
+
// their original status/message survives in the generic error description.
|
|
26
|
+
return loadObjectDefinition(backend.workspace(workspace), id, type).catch((e) => {
|
|
27
|
+
const converted = convertError(e);
|
|
28
|
+
throw isNotFound(converted) ? converted : e;
|
|
29
|
+
});
|
|
24
30
|
},
|
|
25
31
|
}, [backend, workspace, id, type, filled]);
|
|
26
32
|
// Object id is required
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline access row for the metadata tab, wired to the share context. Renders the
|
|
3
|
+
* current access summary, or a placeholder while it loads (the row stays because
|
|
4
|
+
* Share is still reachable for the object), or nothing when sharing is unavailable.
|
|
5
|
+
* Self-subscribing so it re-renders on access edits without re-rendering the tab.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export declare function CatalogItemAccessRow(): import("react/jsx-runtime").JSX.Element | null;
|
|
10
|
+
//# sourceMappingURL=CatalogItemAccessRow.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CatalogItemAccessRow.d.ts","sourceRoot":"","sources":["../../../src/catalogDetail/share/CatalogItemAccessRow.tsx"],"names":[],"mappings":"AAWA;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,mDAoBnC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// (C) 2026 GoodData Corporation
|
|
3
|
+
import { FormattedMessage } from "react-intl";
|
|
4
|
+
import { UiSkeleton } from "@gooddata/sdk-ui-kit";
|
|
5
|
+
import { CatalogDetailContentRow } from "../CatalogDetailContentRow.js";
|
|
6
|
+
import { CatalogDetailAccessRow } from "./CatalogDetailAccessRow.js";
|
|
7
|
+
import { useCatalogItemShareActions, useCatalogItemShareState } from "./CatalogItemShareProvider.js";
|
|
8
|
+
/**
|
|
9
|
+
* Inline access row for the metadata tab, wired to the share context. Renders the
|
|
10
|
+
* current access summary, or a placeholder while it loads (the row stays because
|
|
11
|
+
* Share is still reachable for the object), or nothing when sharing is unavailable.
|
|
12
|
+
* Self-subscribing so it re-renders on access edits without re-rendering the tab.
|
|
13
|
+
*
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
export function CatalogItemAccessRow() {
|
|
17
|
+
const { active, controller } = useCatalogItemShareState();
|
|
18
|
+
const { open } = useCatalogItemShareActions();
|
|
19
|
+
if (!active || !controller) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const summary = controller.state.summary;
|
|
23
|
+
if (summary) {
|
|
24
|
+
return _jsx(CatalogDetailAccessRow, { summary: summary, onOpen: open });
|
|
25
|
+
}
|
|
26
|
+
// Summary not available yet (loading or load error). Keep the row — Share is
|
|
27
|
+
// still reachable — with a placeholder rather than omitting it.
|
|
28
|
+
return (_jsx(CatalogDetailContentRow, { title: _jsx(FormattedMessage, { id: "analyticsCatalog.share.access.row.label" }), content: _jsx(UiSkeleton, { itemWidth: 200, itemHeight: 20 }) }));
|
|
29
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lazy boundary for the catalog item's share dialog. Renders nothing until the
|
|
3
|
+
* dialog is first opened; from then on it stays mounted (so close/reopen is
|
|
4
|
+
* instant) and the dialog's own `isOpen` drives visibility. No-ops when sharing
|
|
5
|
+
* is unavailable.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export declare function CatalogItemShareDialog(): import("react/jsx-runtime").JSX.Element | null;
|
|
10
|
+
//# sourceMappingURL=CatalogItemShareDialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CatalogItemShareDialog.d.ts","sourceRoot":"","sources":["../../../src/catalogDetail/share/CatalogItemShareDialog.tsx"],"names":[],"mappings":"AAaA;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,mDAkBrC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// (C) 2026 GoodData Corporation
|
|
3
|
+
import { Suspense, lazy, useState } from "react";
|
|
4
|
+
import { useCatalogItemShareState } from "./CatalogItemShareProvider.js";
|
|
5
|
+
// Code-split the dialog (and the heavy kit dialog chrome it pulls from
|
|
6
|
+
// @gooddata/sdk-ui-ext) into its own chunk, loaded on first open rather than with
|
|
7
|
+
// the catalog bundle. The always-visible inline access row stays in the main chunk.
|
|
8
|
+
const CatalogItemShareDialogInner = lazy(() => import("./CatalogItemShareDialogInner.js").then((m) => ({ default: m.CatalogItemShareDialogInner })));
|
|
9
|
+
/**
|
|
10
|
+
* Lazy boundary for the catalog item's share dialog. Renders nothing until the
|
|
11
|
+
* dialog is first opened; from then on it stays mounted (so close/reopen is
|
|
12
|
+
* instant) and the dialog's own `isOpen` drives visibility. No-ops when sharing
|
|
13
|
+
* is unavailable.
|
|
14
|
+
*
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
export function CatalogItemShareDialog() {
|
|
18
|
+
const { active, isOpen } = useCatalogItemShareState();
|
|
19
|
+
// Latch: once opened, keep the lazy dialog mounted so reopening doesn't reload
|
|
20
|
+
// the chunk or lose the dialog's transient state.
|
|
21
|
+
const [everOpened, setEverOpened] = useState(false);
|
|
22
|
+
if (isOpen && !everOpened) {
|
|
23
|
+
setEverOpened(true);
|
|
24
|
+
}
|
|
25
|
+
if (!active || !everOpened) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
return (_jsx(Suspense, { fallback: null, children: _jsx(CatalogItemShareDialogInner, {}) }));
|
|
29
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The connected share dialog, split into its own module so it (and the heavy kit
|
|
3
|
+
* dialog chrome it pulls from `@gooddata/sdk-ui-ext`) is code-split out of the main
|
|
4
|
+
* catalog chunk and loaded only when the dialog is first opened. Reads the shared
|
|
5
|
+
* controller from context so it and the inline access row use one access-list fetch.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
export declare function CatalogItemShareDialogInner(): import("react/jsx-runtime").JSX.Element | null;
|
|
10
|
+
//# sourceMappingURL=CatalogItemShareDialogInner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CatalogItemShareDialogInner.d.ts","sourceRoot":"","sources":["../../../src/catalogDetail/share/CatalogItemShareDialogInner.tsx"],"names":[],"mappings":"AAMA;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,mDAkB1C"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// (C) 2026 GoodData Corporation
|
|
3
|
+
import { ObjectShareDialog } from "@gooddata/sdk-ui-ext";
|
|
4
|
+
import { useCatalogItemShareActions, useCatalogItemShareState } from "./CatalogItemShareProvider.js";
|
|
5
|
+
/**
|
|
6
|
+
* The connected share dialog, split into its own module so it (and the heavy kit
|
|
7
|
+
* dialog chrome it pulls from `@gooddata/sdk-ui-ext`) is code-split out of the main
|
|
8
|
+
* catalog chunk and loaded only when the dialog is first opened. Reads the shared
|
|
9
|
+
* controller from context so it and the inline access row use one access-list fetch.
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
export function CatalogItemShareDialogInner() {
|
|
14
|
+
const { controller, target, objectTitle, isOpen, labelsLoading } = useCatalogItemShareState();
|
|
15
|
+
const { close } = useCatalogItemShareActions();
|
|
16
|
+
if (!controller) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
return (_jsx(ObjectShareDialog, { target: target, objectTitle: objectTitle, isOpen: isOpen, onClose: close, labelsLoading: labelsLoading, controller: controller }));
|
|
20
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
import type { IObjectPermissionsObject } from "@gooddata/sdk-backend-spi";
|
|
3
|
+
import { type IObjectShareController } from "@gooddata/sdk-ui-ext";
|
|
4
|
+
import type { ShareableCatalogItem } from "./types.js";
|
|
5
|
+
import type { IShareableLabels } from "./useShareableLabels.js";
|
|
6
|
+
/**
|
|
7
|
+
* Live share state for the catalog item. Consumed by the inline access row and the
|
|
8
|
+
* share dialog, which re-render as the controller's state changes. `active` is false
|
|
9
|
+
* when the item can't be shared (flag off / not a shareable kind), so consumers can
|
|
10
|
+
* render nothing without conditional hooks.
|
|
11
|
+
*/
|
|
12
|
+
interface ICatalogItemShareState {
|
|
13
|
+
active: boolean;
|
|
14
|
+
controller: IObjectShareController | undefined;
|
|
15
|
+
target: IObjectPermissionsObject | undefined;
|
|
16
|
+
objectTitle: string;
|
|
17
|
+
/** Whether the share dialog is open. */
|
|
18
|
+
isOpen: boolean;
|
|
19
|
+
/** Whether the item's labels are still loading (passed to the dialog). */
|
|
20
|
+
labelsLoading: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Stable open/close actions for the share dialog. Split from the state so an
|
|
24
|
+
* actions-only consumer (the Share button) never re-renders on a state tick.
|
|
25
|
+
*/
|
|
26
|
+
interface ICatalogItemShareActions {
|
|
27
|
+
active: boolean;
|
|
28
|
+
open: () => void;
|
|
29
|
+
close: () => void;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* @internal
|
|
33
|
+
*/
|
|
34
|
+
export interface ICatalogItemShareProviderProps {
|
|
35
|
+
/** The shareable item, or undefined when sharing is unavailable (flag off / not shareable). */
|
|
36
|
+
shareableItem: ShareableCatalogItem | undefined;
|
|
37
|
+
/** Backend share target for {@link shareableItem}. */
|
|
38
|
+
target: IObjectPermissionsObject | undefined;
|
|
39
|
+
/** Labels of the shared attribute (already fetched by the parent, reused here). */
|
|
40
|
+
labels: IShareableLabels;
|
|
41
|
+
children: ReactNode;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Owns the share controller, its single access-list fetch and the dialog open
|
|
45
|
+
* state for one catalog item, and exposes them through two contexts (state +
|
|
46
|
+
* actions). Because it renders `children` and the live state is read only by the
|
|
47
|
+
* leaf components that need it (access row, dialog), the surrounding detail
|
|
48
|
+
* subtree — header, tabs, status — does not re-render as access is edited.
|
|
49
|
+
*
|
|
50
|
+
* No-ops cleanly when the item can't be shared: the contexts then carry an
|
|
51
|
+
* inactive value and the hooks below report `active: false`.
|
|
52
|
+
*
|
|
53
|
+
* @internal
|
|
54
|
+
*/
|
|
55
|
+
export declare function CatalogItemShareProvider({ shareableItem, target, labels, children }: ICatalogItemShareProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
56
|
+
/**
|
|
57
|
+
* Live share state (controller, summary, open flag) for the current catalog item.
|
|
58
|
+
* Reports `active: false` when sharing is unavailable.
|
|
59
|
+
*
|
|
60
|
+
* @internal
|
|
61
|
+
*/
|
|
62
|
+
export declare function useCatalogItemShareState(): ICatalogItemShareState;
|
|
63
|
+
/**
|
|
64
|
+
* Stable share actions (open/close the dialog) for the current catalog item.
|
|
65
|
+
* Reading this instead of the state keeps a consumer from re-rendering on state ticks.
|
|
66
|
+
*
|
|
67
|
+
* @internal
|
|
68
|
+
*/
|
|
69
|
+
export declare function useCatalogItemShareActions(): ICatalogItemShareActions;
|
|
70
|
+
export {};
|
|
71
|
+
//# sourceMappingURL=CatalogItemShareProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CatalogItemShareProvider.d.ts","sourceRoot":"","sources":["../../../src/catalogDetail/share/CatalogItemShareProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAA6D,MAAM,OAAO,CAAC;AAElG,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAC1E,OAAO,EAAE,KAAK,sBAAsB,EAAkB,MAAM,sBAAsB,CAAC;AAEnF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhE;;;;;GAKG;AACH,UAAU,sBAAsB;IAC5B,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,sBAAsB,GAAG,SAAS,CAAC;IAC/C,MAAM,EAAE,wBAAwB,GAAG,SAAS,CAAC;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,MAAM,EAAE,OAAO,CAAC;IAChB,0EAA0E;IAC1E,aAAa,EAAE,OAAO,CAAC;CAC1B;AAED;;;GAGG;AACH,UAAU,wBAAwB;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,KAAK,EAAE,MAAM,IAAI,CAAC;CACrB;AAiBD;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC3C,+FAA+F;IAC/F,aAAa,EAAE,oBAAoB,GAAG,SAAS,CAAC;IAChD,sDAAsD;IACtD,MAAM,EAAE,wBAAwB,GAAG,SAAS,CAAC;IAC7C,mFAAmF;IACnF,MAAM,EAAE,gBAAgB,CAAC;IACzB,QAAQ,EAAE,SAAS,CAAC;CACvB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CAAC,EACrC,aAAa,EACb,MAAM,EACN,MAAM,EACN,QAAQ,EACX,EAAE,8BAA8B,2CAmDhC;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,IAAI,sBAAsB,CAEjE;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,IAAI,wBAAwB,CAErE"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
// (C) 2026 GoodData Corporation
|
|
3
|
+
import { createContext, useCallback, useContext, useMemo, useState } from "react";
|
|
4
|
+
import { useObjectShare } from "@gooddata/sdk-ui-ext";
|
|
5
|
+
const INACTIVE_STATE = {
|
|
6
|
+
active: false,
|
|
7
|
+
controller: undefined,
|
|
8
|
+
target: undefined,
|
|
9
|
+
objectTitle: "",
|
|
10
|
+
isOpen: false,
|
|
11
|
+
labelsLoading: false,
|
|
12
|
+
};
|
|
13
|
+
const noop = () => { };
|
|
14
|
+
const INACTIVE_ACTIONS = { active: false, open: noop, close: noop };
|
|
15
|
+
const ShareStateContext = createContext(INACTIVE_STATE);
|
|
16
|
+
const ShareActionsContext = createContext(INACTIVE_ACTIONS);
|
|
17
|
+
/**
|
|
18
|
+
* Owns the share controller, its single access-list fetch and the dialog open
|
|
19
|
+
* state for one catalog item, and exposes them through two contexts (state +
|
|
20
|
+
* actions). Because it renders `children` and the live state is read only by the
|
|
21
|
+
* leaf components that need it (access row, dialog), the surrounding detail
|
|
22
|
+
* subtree — header, tabs, status — does not re-render as access is edited.
|
|
23
|
+
*
|
|
24
|
+
* No-ops cleanly when the item can't be shared: the contexts then carry an
|
|
25
|
+
* inactive value and the hooks below report `active: false`.
|
|
26
|
+
*
|
|
27
|
+
* @internal
|
|
28
|
+
*/
|
|
29
|
+
export function CatalogItemShareProvider({ shareableItem, target, labels, children, }) {
|
|
30
|
+
// One controller drives both the dialog and the inline access row; the row reads
|
|
31
|
+
// `state.summary`, so a save inside the dialog refreshes it too. labelsLoading/
|
|
32
|
+
// labelsError keep the controller label-unresolved while labels are pending.
|
|
33
|
+
const controller = useObjectShare(target, {
|
|
34
|
+
labels: labels.labels,
|
|
35
|
+
labelsError: labels.error,
|
|
36
|
+
labelsLoading: labels.loading,
|
|
37
|
+
});
|
|
38
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
39
|
+
// The detail view is reused as the user navigates between objects, so close an
|
|
40
|
+
// open dialog when the shareable target changes (or becomes non-shareable) —
|
|
41
|
+
// otherwise it would linger open on the next object. Reset during render (React's
|
|
42
|
+
// "adjust state on prop change" idiom) rather than in an effect.
|
|
43
|
+
const targetKey = shareableItem?.identifier;
|
|
44
|
+
const [openForKey, setOpenForKey] = useState(undefined);
|
|
45
|
+
if (isOpen && openForKey !== targetKey) {
|
|
46
|
+
setIsOpen(false);
|
|
47
|
+
}
|
|
48
|
+
const open = useCallback(() => {
|
|
49
|
+
setOpenForKey(targetKey);
|
|
50
|
+
setIsOpen(true);
|
|
51
|
+
}, [targetKey]);
|
|
52
|
+
const close = useCallback(() => setIsOpen(false), []);
|
|
53
|
+
const state = useMemo(() => shareableItem
|
|
54
|
+
? {
|
|
55
|
+
active: true,
|
|
56
|
+
controller,
|
|
57
|
+
target,
|
|
58
|
+
objectTitle: shareableItem.title,
|
|
59
|
+
isOpen,
|
|
60
|
+
labelsLoading: labels.loading,
|
|
61
|
+
}
|
|
62
|
+
: INACTIVE_STATE, [shareableItem, controller, target, isOpen, labels.loading]);
|
|
63
|
+
const actions = useMemo(() => (shareableItem ? { active: true, open, close } : INACTIVE_ACTIONS), [shareableItem, open, close]);
|
|
64
|
+
return (_jsx(ShareActionsContext.Provider, { value: actions, children: _jsx(ShareStateContext.Provider, { value: state, children: children }) }));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Live share state (controller, summary, open flag) for the current catalog item.
|
|
68
|
+
* Reports `active: false` when sharing is unavailable.
|
|
69
|
+
*
|
|
70
|
+
* @internal
|
|
71
|
+
*/
|
|
72
|
+
export function useCatalogItemShareState() {
|
|
73
|
+
return useContext(ShareStateContext);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Stable share actions (open/close the dialog) for the current catalog item.
|
|
77
|
+
* Reading this instead of the state keeps a consumer from re-rendering on state ticks.
|
|
78
|
+
*
|
|
79
|
+
* @internal
|
|
80
|
+
*/
|
|
81
|
+
export function useCatalogItemShareActions() {
|
|
82
|
+
return useContext(ShareActionsContext);
|
|
83
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gooddata/sdk-ui-catalog",
|
|
3
|
-
"version": "11.43.0-alpha.
|
|
3
|
+
"version": "11.43.0-alpha.4",
|
|
4
4
|
"description": "GoodData SDK - Analytics Catalog",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "GoodData Corporation",
|
|
@@ -45,14 +45,14 @@
|
|
|
45
45
|
"tslib": "2.8.1",
|
|
46
46
|
"yaml": "2.8.3",
|
|
47
47
|
"zod": "4.3.6",
|
|
48
|
-
"@gooddata/sdk-backend-spi": "11.43.0-alpha.
|
|
49
|
-
"@gooddata/sdk-model": "11.43.0-alpha.
|
|
50
|
-
"@gooddata/sdk-ui
|
|
51
|
-
"@gooddata/sdk-ui": "11.43.0-alpha.
|
|
52
|
-
"@gooddata/sdk-ui-
|
|
53
|
-
"@gooddata/sdk-ui-
|
|
54
|
-
"@gooddata/sdk-ui-
|
|
55
|
-
"@gooddata/util": "11.43.0-alpha.
|
|
48
|
+
"@gooddata/sdk-backend-spi": "11.43.0-alpha.4",
|
|
49
|
+
"@gooddata/sdk-model": "11.43.0-alpha.4",
|
|
50
|
+
"@gooddata/sdk-ui": "11.43.0-alpha.4",
|
|
51
|
+
"@gooddata/sdk-ui-kit": "11.43.0-alpha.4",
|
|
52
|
+
"@gooddata/sdk-ui-ext": "11.43.0-alpha.4",
|
|
53
|
+
"@gooddata/sdk-ui-semantic-search": "11.43.0-alpha.4",
|
|
54
|
+
"@gooddata/sdk-ui-theme-provider": "11.43.0-alpha.4",
|
|
55
|
+
"@gooddata/util": "11.43.0-alpha.4"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@microsoft/api-documenter": "^7.17.0",
|
|
@@ -92,11 +92,11 @@
|
|
|
92
92
|
"typescript": "5.9.3",
|
|
93
93
|
"vitest": "4.1.8",
|
|
94
94
|
"vitest-dom": "0.1.1",
|
|
95
|
-
"@gooddata/
|
|
96
|
-
"@gooddata/
|
|
97
|
-
"@gooddata/oxlint-config": "11.43.0-alpha.
|
|
98
|
-
"@gooddata/
|
|
99
|
-
"@gooddata/
|
|
95
|
+
"@gooddata/eslint-config": "11.43.0-alpha.4",
|
|
96
|
+
"@gooddata/i18n-toolkit": "11.43.0-alpha.4",
|
|
97
|
+
"@gooddata/oxlint-config": "11.43.0-alpha.4",
|
|
98
|
+
"@gooddata/sdk-backend-mockingbird": "11.43.0-alpha.4",
|
|
99
|
+
"@gooddata/stylelint-config": "11.43.0-alpha.4"
|
|
100
100
|
},
|
|
101
101
|
"peerDependencies": {
|
|
102
102
|
"react": "^18.0.0 || ^19.0.0",
|