@gooddata/sdk-ui-catalog 11.51.0-alpha.1 → 11.51.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/esm/catalogDetail/share/CatalogDetailAccessRow.d.ts.map +1 -1
  2. package/esm/catalogDetail/share/CatalogDetailAccessRow.js +9 -5
  3. package/esm/catalogDetail/share/CatalogDetailLabels.d.ts +2 -2
  4. package/esm/catalogDetail/share/CatalogDetailLabels.d.ts.map +1 -1
  5. package/esm/catalogDetail/share/CatalogDetailLabels.js +6 -2
  6. package/esm/catalogDetail/share/CatalogItemAccessRow.d.ts +5 -3
  7. package/esm/catalogDetail/share/CatalogItemAccessRow.d.ts.map +1 -1
  8. package/esm/catalogDetail/share/CatalogItemAccessRow.js +12 -10
  9. package/esm/catalogDetail/share/CatalogItemShareDialog.d.ts +4 -4
  10. package/esm/catalogDetail/share/CatalogItemShareDialog.d.ts.map +1 -1
  11. package/esm/catalogDetail/share/CatalogItemShareDialog.js +6 -12
  12. package/esm/catalogDetail/share/CatalogItemShareDialogInner.d.ts +4 -3
  13. package/esm/catalogDetail/share/CatalogItemShareDialogInner.d.ts.map +1 -1
  14. package/esm/catalogDetail/share/CatalogItemShareDialogInner.js +6 -8
  15. package/esm/catalogDetail/share/CatalogItemShareProvider.d.ts +36 -21
  16. package/esm/catalogDetail/share/CatalogItemShareProvider.d.ts.map +1 -1
  17. package/esm/catalogDetail/share/CatalogItemShareProvider.js +80 -47
  18. package/esm/catalogDetail/share/messages.d.ts +6 -0
  19. package/esm/catalogDetail/share/messages.d.ts.map +1 -1
  20. package/esm/catalogDetail/share/messages.js +2 -0
  21. package/esm/catalogDetail/share/useShareableLabels.d.ts +1 -1
  22. package/esm/catalogDetail/share/useShareableLabels.d.ts.map +1 -1
  23. package/esm/catalogDetail/share/useShareableLabels.js +7 -2
  24. package/esm/localization/bundles/de-DE.localization-bundle.d.ts +2 -0
  25. package/esm/localization/bundles/de-DE.localization-bundle.d.ts.map +1 -1
  26. package/esm/localization/bundles/de-DE.localization-bundle.js +3 -1
  27. package/esm/localization/bundles/en-AU.localization-bundle.d.ts +2 -0
  28. package/esm/localization/bundles/en-AU.localization-bundle.d.ts.map +1 -1
  29. package/esm/localization/bundles/en-AU.localization-bundle.js +2 -0
  30. package/esm/localization/bundles/en-GB.localization-bundle.d.ts +2 -0
  31. package/esm/localization/bundles/en-GB.localization-bundle.d.ts.map +1 -1
  32. package/esm/localization/bundles/en-GB.localization-bundle.js +3 -1
  33. package/esm/localization/bundles/en-US.localization-bundle.d.ts +8 -0
  34. package/esm/localization/bundles/en-US.localization-bundle.d.ts.map +1 -1
  35. package/esm/localization/bundles/en-US.localization-bundle.js +9 -1
  36. package/esm/localization/bundles/es-419.localization-bundle.d.ts +2 -0
  37. package/esm/localization/bundles/es-419.localization-bundle.d.ts.map +1 -1
  38. package/esm/localization/bundles/es-419.localization-bundle.js +3 -1
  39. package/esm/localization/bundles/es-ES.localization-bundle.d.ts +2 -0
  40. package/esm/localization/bundles/es-ES.localization-bundle.d.ts.map +1 -1
  41. package/esm/localization/bundles/es-ES.localization-bundle.js +3 -1
  42. package/esm/localization/bundles/fi-FI.localization-bundle.d.ts +2 -0
  43. package/esm/localization/bundles/fi-FI.localization-bundle.d.ts.map +1 -1
  44. package/esm/localization/bundles/fi-FI.localization-bundle.js +5 -3
  45. package/esm/localization/bundles/fr-CA.localization-bundle.d.ts +2 -0
  46. package/esm/localization/bundles/fr-CA.localization-bundle.d.ts.map +1 -1
  47. package/esm/localization/bundles/fr-CA.localization-bundle.js +3 -1
  48. package/esm/localization/bundles/fr-FR.localization-bundle.d.ts +2 -0
  49. package/esm/localization/bundles/fr-FR.localization-bundle.d.ts.map +1 -1
  50. package/esm/localization/bundles/fr-FR.localization-bundle.js +3 -1
  51. package/esm/localization/bundles/id-ID.localization-bundle.d.ts +2 -0
  52. package/esm/localization/bundles/id-ID.localization-bundle.d.ts.map +1 -1
  53. package/esm/localization/bundles/id-ID.localization-bundle.js +3 -1
  54. package/esm/localization/bundles/it-IT.localization-bundle.d.ts +2 -0
  55. package/esm/localization/bundles/it-IT.localization-bundle.d.ts.map +1 -1
  56. package/esm/localization/bundles/it-IT.localization-bundle.js +3 -1
  57. package/esm/localization/bundles/ja-JP.localization-bundle.d.ts +2 -0
  58. package/esm/localization/bundles/ja-JP.localization-bundle.d.ts.map +1 -1
  59. package/esm/localization/bundles/ja-JP.localization-bundle.js +3 -1
  60. package/esm/localization/bundles/ko-KR.localization-bundle.d.ts +2 -0
  61. package/esm/localization/bundles/ko-KR.localization-bundle.d.ts.map +1 -1
  62. package/esm/localization/bundles/ko-KR.localization-bundle.js +3 -1
  63. package/esm/localization/bundles/nl-NL.localization-bundle.d.ts +2 -0
  64. package/esm/localization/bundles/nl-NL.localization-bundle.d.ts.map +1 -1
  65. package/esm/localization/bundles/nl-NL.localization-bundle.js +3 -1
  66. package/esm/localization/bundles/pl-PL.localization-bundle.d.ts +2 -0
  67. package/esm/localization/bundles/pl-PL.localization-bundle.d.ts.map +1 -1
  68. package/esm/localization/bundles/pl-PL.localization-bundle.js +2 -0
  69. package/esm/localization/bundles/pt-BR.localization-bundle.d.ts +2 -0
  70. package/esm/localization/bundles/pt-BR.localization-bundle.d.ts.map +1 -1
  71. package/esm/localization/bundles/pt-BR.localization-bundle.js +3 -1
  72. package/esm/localization/bundles/pt-PT.localization-bundle.d.ts +2 -0
  73. package/esm/localization/bundles/pt-PT.localization-bundle.d.ts.map +1 -1
  74. package/esm/localization/bundles/pt-PT.localization-bundle.js +5 -3
  75. package/esm/localization/bundles/ru-RU.localization-bundle.d.ts +2 -0
  76. package/esm/localization/bundles/ru-RU.localization-bundle.d.ts.map +1 -1
  77. package/esm/localization/bundles/ru-RU.localization-bundle.js +3 -1
  78. package/esm/localization/bundles/sl-SI.localization-bundle.d.ts +2 -0
  79. package/esm/localization/bundles/sl-SI.localization-bundle.d.ts.map +1 -1
  80. package/esm/localization/bundles/sl-SI.localization-bundle.js +3 -1
  81. package/esm/localization/bundles/th-TH.localization-bundle.d.ts +2 -0
  82. package/esm/localization/bundles/th-TH.localization-bundle.d.ts.map +1 -1
  83. package/esm/localization/bundles/th-TH.localization-bundle.js +2 -0
  84. package/esm/localization/bundles/tr-TR.localization-bundle.d.ts +2 -0
  85. package/esm/localization/bundles/tr-TR.localization-bundle.d.ts.map +1 -1
  86. package/esm/localization/bundles/tr-TR.localization-bundle.js +3 -1
  87. package/esm/localization/bundles/uk-UA.localization-bundle.d.ts +2 -0
  88. package/esm/localization/bundles/uk-UA.localization-bundle.d.ts.map +1 -1
  89. package/esm/localization/bundles/uk-UA.localization-bundle.js +4 -2
  90. package/esm/localization/bundles/vi-VN.localization-bundle.d.ts +2 -0
  91. package/esm/localization/bundles/vi-VN.localization-bundle.d.ts.map +1 -1
  92. package/esm/localization/bundles/vi-VN.localization-bundle.js +3 -1
  93. package/esm/localization/bundles/zh-HK.localization-bundle.d.ts +2 -0
  94. package/esm/localization/bundles/zh-HK.localization-bundle.d.ts.map +1 -1
  95. package/esm/localization/bundles/zh-HK.localization-bundle.js +2 -0
  96. package/esm/localization/bundles/zh-Hans.localization-bundle.d.ts +2 -0
  97. package/esm/localization/bundles/zh-Hans.localization-bundle.d.ts.map +1 -1
  98. package/esm/localization/bundles/zh-Hans.localization-bundle.js +3 -1
  99. package/esm/localization/bundles/zh-Hant.localization-bundle.d.ts +2 -0
  100. package/esm/localization/bundles/zh-Hant.localization-bundle.d.ts.map +1 -1
  101. package/esm/localization/bundles/zh-Hant.localization-bundle.js +3 -1
  102. package/package.json +15 -15
@@ -1 +1 @@
1
- {"version":3,"file":"CatalogDetailAccessRow.d.ts","sourceRoot":"","sources":["../../../src/catalogDetail/share/CatalogDetailAccessRow.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAYjE;;GAEG;AACH,MAAM,WAAW,4BAA4B;IACzC,OAAO,EAAE,oBAAoB,CAAC;IAC9B,MAAM,EAAE,MAAM,IAAI,CAAC;CACtB;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,4BAA4B,2CA+CvF"}
1
+ {"version":3,"file":"CatalogDetailAccessRow.d.ts","sourceRoot":"","sources":["../../../src/catalogDetail/share/CatalogDetailAccessRow.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAYjE;;GAEG;AACH,MAAM,WAAW,4BAA4B;IACzC,OAAO,EAAE,oBAAoB,CAAC;IAC9B,MAAM,EAAE,MAAM,IAAI,CAAC;CACtB;AASD;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,4BAA4B,2CA8CvF"}
@@ -5,6 +5,12 @@ import { UiIcon } from "@gooddata/sdk-ui-kit";
5
5
  import { catalogDetailAccessRow, catalogDetailAccessRowSharedLink, catalogDetailAccessRowState, } from "../../automation/testIds.js";
6
6
  import { CatalogDetailContentRow } from "../CatalogDetailContentRow.js";
7
7
  import { shareMessages } from "./messages.js";
8
+ // Chip copy for each workspace-wide access level.
9
+ const WORKSPACE_ACCESS_ROW_MESSAGE = {
10
+ EDIT: shareMessages.accessRowWorkspaceEdit,
11
+ SHARE: shareMessages.accessRowWorkspaceShare,
12
+ VIEW: shareMessages.accessRowWorkspaceView,
13
+ };
8
14
  /**
9
15
  * Inline metadata row showing the current access state. Composed of two chips:
10
16
  *
@@ -17,11 +23,9 @@ import { shareMessages } from "./messages.js";
17
23
  export function CatalogDetailAccessRow({ summary, onOpen }) {
18
24
  const intl = useIntl();
19
25
  const { generalAccess, workspaceLevel, granteeCount } = summary;
20
- const stateText = generalAccess === "RESTRICTED"
21
- ? intl.formatMessage(shareMessages.accessRowRestricted)
22
- : workspaceLevel === "SHARE"
23
- ? intl.formatMessage(shareMessages.accessRowWorkspaceShare)
24
- : intl.formatMessage(shareMessages.accessRowWorkspaceView);
26
+ const stateText = intl.formatMessage(generalAccess === "RESTRICTED"
27
+ ? shareMessages.accessRowRestricted
28
+ : WORKSPACE_ACCESS_ROW_MESSAGE[workspaceLevel]);
25
29
  const stateIcon = generalAccess === "RESTRICTED" ? "lock" : "building";
26
30
  return (_jsx(CatalogDetailContentRow, { title: _jsx(FormattedMessage, { ...shareMessages.accessRowLabel }), content: _jsxs("div", { className: "gd-analytics-catalog-detail__access-row", "data-testid": catalogDetailAccessRow, children: [
27
31
  _jsxs("span", { className: "gd-analytics-catalog-detail__access-row__chip", "data-testid": catalogDetailAccessRowState, children: [
@@ -1,9 +1,9 @@
1
- import type { IObjectShareLabel } from "@gooddata/sdk-ui-ext";
1
+ import { type IObjectShareLabel } from "@gooddata/sdk-ui-ext";
2
2
  /**
3
3
  * @internal
4
4
  */
5
5
  export interface ICatalogDetailLabelsProps {
6
- /** Labels of the attribute, primary first is not required sorted here. */
6
+ /** Labels of the attribute, already in display order (primary first, then alphabetical). */
7
7
  labels: IObjectShareLabel[];
8
8
  }
9
9
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"CatalogDetailLabels.d.ts","sourceRoot":"","sources":["../../../src/catalogDetail/share/CatalogDetailLabels.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAW9D;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACtC,8EAA4E;IAC5E,MAAM,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,MAAM,EAAE,EAAE,yBAAyB,kDA6DxE"}
1
+ {"version":3,"file":"CatalogDetailLabels.d.ts","sourceRoot":"","sources":["../../../src/catalogDetail/share/CatalogDetailLabels.tsx"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,iBAAiB,EAAuB,MAAM,sBAAsB,CAAC;AAWnF;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACtC,4FAA4F;IAC5F,MAAM,EAAE,iBAAiB,EAAE,CAAC;CAC/B;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,MAAM,EAAE,EAAE,yBAAyB,kDA6DxE"}
@@ -2,6 +2,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
2
2
  // (C) 2026 GoodData Corporation
3
3
  import { useMemo } from "react";
4
4
  import { useIntl } from "react-intl";
5
+ import { sortShareableLabels } from "@gooddata/sdk-ui-ext";
5
6
  import { UiLabelsList, UiPopover } from "@gooddata/sdk-ui-kit";
6
7
  import { catalogDetailLabels, catalogDetailLabelsList, catalogDetailLabelsTrigger, } from "../../automation/testIds.js";
7
8
  import { labelsMessages } from "./messages.js";
@@ -14,8 +15,11 @@ import { labelsMessages } from "./messages.js";
14
15
  */
15
16
  export function CatalogDetailLabels({ labels }) {
16
17
  const intl = useIntl();
17
- // Primary label leads the summary and the list; the rest follow in source order.
18
- const ordered = useMemo(() => [...labels].sort((a, b) => Number(b.isPrimary) - Number(a.isPrimary)), [labels]);
18
+ // Deterministic display order (primary first, then alphabetical) via the
19
+ // shared helper the same order the share dialog's label-access checklist
20
+ // uses, so the two lists never disagree. Sorting here as well keeps the
21
+ // component robust to an unsorted input.
22
+ const ordered = useMemo(() => sortShareableLabels(labels), [labels]);
19
23
  const items = useMemo(() => ordered.map((label) => ({
20
24
  id: label.id,
21
25
  label: label.title,
@@ -1,8 +1,10 @@
1
1
  /**
2
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.
3
+ * current access summary, a placeholder while it loads, or an error note when the
4
+ * summary fetch failed (the row stays in both cases because Share is still
5
+ * reachable for the object opening the dialog fetches again and fills the
6
+ * summary in). Renders nothing when sharing is unavailable. Self-subscribing so
7
+ * it re-renders on access edits without re-rendering the tab.
6
8
  *
7
9
  * @internal
8
10
  */
@@ -1 +1 @@
1
- {"version":3,"file":"CatalogItemAccessRow.d.ts","sourceRoot":"","sources":["../../../src/catalogDetail/share/CatalogItemAccessRow.tsx"],"names":[],"mappings":"AAWA;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,mDAqBnC"}
1
+ {"version":3,"file":"CatalogItemAccessRow.d.ts","sourceRoot":"","sources":["../../../src/catalogDetail/share/CatalogItemAccessRow.tsx"],"names":[],"mappings":"AAYA;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,mDA2BnC"}
@@ -5,26 +5,28 @@ import { UiSkeleton } from "@gooddata/sdk-ui-kit";
5
5
  import { CatalogDetailContentRow } from "../CatalogDetailContentRow.js";
6
6
  import { CatalogDetailAccessRow } from "./CatalogDetailAccessRow.js";
7
7
  import { useCatalogItemShareActions, useCatalogItemShareState } from "./CatalogItemShareProvider.js";
8
+ import { shareMessages } from "./messages.js";
8
9
  /**
9
10
  * 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.
11
+ * current access summary, a placeholder while it loads, or an error note when the
12
+ * summary fetch failed (the row stays in both cases because Share is still
13
+ * reachable for the object opening the dialog fetches again and fills the
14
+ * summary in). Renders nothing when sharing is unavailable. Self-subscribing so
15
+ * it re-renders on access edits without re-rendering the tab.
13
16
  *
14
17
  * @internal
15
18
  */
16
19
  export function CatalogItemAccessRow() {
17
- const { active, controller } = useCatalogItemShareState();
20
+ const { active, summary, summaryError } = useCatalogItemShareState();
18
21
  const { open } = useCatalogItemShareActions();
19
- if (!active || !controller) {
22
+ if (!active) {
20
23
  return null;
21
24
  }
22
- const summary = controller.state.summary;
23
25
  if (summary) {
24
26
  return _jsx(CatalogDetailAccessRow, { summary: summary, onOpen: open });
25
27
  }
26
- // Summary not available yet still loading. A definitive not-permissionable
27
- // load failure makes the provider inactive (the row is gone via the guard
28
- // above), so this placeholder only covers the genuine loading window.
29
- return (_jsx(CatalogDetailContentRow, { title: _jsx(FormattedMessage, { id: "analyticsCatalog.share.access.row.label" }), content: _jsx(UiSkeleton, { itemWidth: 200, itemHeight: 20 }) }));
28
+ return (_jsx(CatalogDetailContentRow, { title: _jsx(FormattedMessage, { ...shareMessages.accessRowLabel }), content: summaryError ? (
29
+ // role="alert": the error replaces the skeleton after an async
30
+ // failure it must be announced, not just inserted.
31
+ _jsx("span", { role: "alert", children: _jsx(FormattedMessage, { ...shareMessages.accessRowError }) })) : (_jsx(UiSkeleton, { itemWidth: 200, itemHeight: 20 })) }));
30
32
  }
@@ -1,8 +1,8 @@
1
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.
2
+ * Lazy boundary for the catalog item's share dialog. Mounted only while the dialog
3
+ * is open one mount is one dialog session, and unmounting (close or navigation)
4
+ * discards the session's transient state. Reopening remounts the component; the
5
+ * chunk itself stays cached by the browser after the first open.
6
6
  *
7
7
  * @internal
8
8
  */
@@ -1 +1 @@
1
- {"version":3,"file":"CatalogItemShareDialog.d.ts","sourceRoot":"","sources":["../../../src/catalogDetail/share/CatalogItemShareDialog.tsx"],"names":[],"mappings":"AAaA;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,mDAkBrC"}
1
+ {"version":3,"file":"CatalogItemShareDialog.d.ts","sourceRoot":"","sources":["../../../src/catalogDetail/share/CatalogItemShareDialog.tsx"],"names":[],"mappings":"AAaA;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,mDAYrC"}
@@ -1,28 +1,22 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  // (C) 2026 GoodData Corporation
3
- import { Suspense, lazy, useState } from "react";
3
+ import { Suspense, lazy } from "react";
4
4
  import { useCatalogItemShareState } from "./CatalogItemShareProvider.js";
5
5
  // Code-split the dialog (and the heavy kit dialog chrome it pulls from
6
6
  // @gooddata/sdk-ui-ext) into its own chunk, loaded on first open rather than with
7
7
  // the catalog bundle. The always-visible inline access row stays in the main chunk.
8
8
  const CatalogItemShareDialogInner = lazy(() => import("./CatalogItemShareDialogInner.js").then((m) => ({ default: m.CatalogItemShareDialogInner })));
9
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.
10
+ * Lazy boundary for the catalog item's share dialog. Mounted only while the dialog
11
+ * is open one mount is one dialog session, and unmounting (close or navigation)
12
+ * discards the session's transient state. Reopening remounts the component; the
13
+ * chunk itself stays cached by the browser after the first open.
14
14
  *
15
15
  * @internal
16
16
  */
17
17
  export function CatalogItemShareDialog() {
18
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) {
19
+ if (!active || !isOpen) {
26
20
  return null;
27
21
  }
28
22
  return (_jsx(Suspense, { fallback: null, children: _jsx(CatalogItemShareDialogInner, {}) }));
@@ -1,10 +1,11 @@
1
1
  /**
2
2
  * The connected share dialog, split into its own module so it (and the heavy kit
3
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.
4
+ * catalog chunk and loaded only when the dialog is first opened. Mounted per dialog
5
+ * session (see {@link CatalogItemShareDialog}); it reports summary changes back to
6
+ * the provider so the inline access row stays in sync without a refetch.
6
7
  *
7
8
  * @internal
8
9
  */
9
- export declare function CatalogItemShareDialogInner(): import("react/jsx-runtime").JSX.Element | null;
10
+ export declare function CatalogItemShareDialogInner(): import("react/jsx-runtime").JSX.Element;
10
11
  //# sourceMappingURL=CatalogItemShareDialogInner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CatalogItemShareDialogInner.d.ts","sourceRoot":"","sources":["../../../src/catalogDetail/share/CatalogItemShareDialogInner.tsx"],"names":[],"mappings":"AAMA;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,mDAkB1C"}
1
+ {"version":3,"file":"CatalogItemShareDialogInner.d.ts","sourceRoot":"","sources":["../../../src/catalogDetail/share/CatalogItemShareDialogInner.tsx"],"names":[],"mappings":"AAMA;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,4CAgB1C"}
@@ -5,16 +5,14 @@ import { useCatalogItemShareActions, useCatalogItemShareState } from "./CatalogI
5
5
  /**
6
6
  * The connected share dialog, split into its own module so it (and the heavy kit
7
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.
8
+ * catalog chunk and loaded only when the dialog is first opened. Mounted per dialog
9
+ * session (see {@link CatalogItemShareDialog}); it reports summary changes back to
10
+ * the provider so the inline access row stays in sync without a refetch.
10
11
  *
11
12
  * @internal
12
13
  */
13
14
  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 }));
15
+ const { target, objectTitle, labels } = useCatalogItemShareState();
16
+ const { close, onSummaryChange } = useCatalogItemShareActions();
17
+ return (_jsx(ObjectShareDialog, { target: target, objectTitle: objectTitle, isOpen: true, onClose: close, labels: labels.labels, labelsError: labels.error, labelsLoading: labels.loading, onSummaryChange: onSummaryChange }));
20
18
  }
@@ -1,32 +1,45 @@
1
1
  import { type ReactNode } from "react";
2
2
  import type { IObjectPermissionsObject } from "@gooddata/sdk-backend-spi";
3
- import { type IObjectShareController } from "@gooddata/sdk-ui-ext";
3
+ import { type IObjectAccessSummary } from "@gooddata/sdk-ui-ext";
4
4
  import type { ShareableCatalogItem } from "./types.js";
5
5
  import type { IShareableLabels } from "./useShareableLabels.js";
6
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.
7
+ * Page-level share state for the catalog item: the latest known access summary and
8
+ * the dialog-open flag. `active` is false when the item can't be shared (flag off /
9
+ * not a shareable kind / permissions unavailable), so consumers can render nothing
10
+ * without conditional hooks.
11
11
  */
12
12
  interface ICatalogItemShareState {
13
13
  active: boolean;
14
- controller: IObjectShareController | undefined;
14
+ /**
15
+ * Latest known access summary — seeded by the page-level fetch, then kept in
16
+ * sync by the open dialog's `onSummaryChange`. Undefined while loading.
17
+ */
18
+ summary: IObjectAccessSummary | undefined;
19
+ /**
20
+ * Whether the page-level summary fetch failed transiently (a definitive
21
+ * not-permissionable failure turns the whole feature inactive instead).
22
+ * Sharing stays offered — opening the dialog runs its own fetch, whose
23
+ * `onSummaryChange` report then fills the summary in.
24
+ */
25
+ summaryError: boolean;
15
26
  target: IObjectPermissionsObject | undefined;
16
27
  objectTitle: string;
17
- /** Whether the share dialog is open. */
28
+ /** Whether the share dialog is open (and should be mounted). */
18
29
  isOpen: boolean;
19
- /** Whether the item's labels are still loading (passed to the dialog). */
20
- labelsLoading: boolean;
30
+ /** The item's labels bundle, forwarded to the dialog. */
31
+ labels: IShareableLabels;
21
32
  }
22
33
  /**
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.
34
+ * Stable share actions. Split from the state so an actions-only consumer (the
35
+ * Share button) never re-renders on a state tick.
25
36
  */
26
37
  interface ICatalogItemShareActions {
27
38
  active: boolean;
28
39
  open: () => void;
29
40
  close: () => void;
41
+ /** The dialog reports summary changes here, keeping the inline row in sync without refetching. */
42
+ onSummaryChange: (summary: IObjectAccessSummary) => void;
30
43
  }
31
44
  /**
32
45
  * @internal
@@ -41,28 +54,30 @@ export interface ICatalogItemShareProviderProps {
41
54
  children: ReactNode;
42
55
  }
43
56
  /**
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
- * subtreeheader, tabs, status does not re-render as access is edited.
57
+ * Owns the PAGE state of sharing for one catalog item: the initial access-summary
58
+ * fetch (the inline access row is visible before the dialog ever opens, so the page
59
+ * fetches its own summary) and the dialog-open flag. The dialog itself is a separate,
60
+ * session-scoped component mounted only while open, owning its own controller and
61
+ * fetchthat reports summary changes back through `onSummaryChange`, so the row
62
+ * stays in sync with edits without a refetch.
49
63
  *
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`.
64
+ * No-ops cleanly when the item can't be shared: the contexts then carry an inactive
65
+ * value and the hooks below report `active: false`.
52
66
  *
53
67
  * @internal
54
68
  */
55
69
  export declare function CatalogItemShareProvider({ shareableItem, target, labels, children }: ICatalogItemShareProviderProps): import("react/jsx-runtime").JSX.Element;
56
70
  /**
57
- * Live share state (controller, summary, open flag) for the current catalog item.
71
+ * Page share state (summary, open flag, dialog inputs) for the current catalog item.
58
72
  * Reports `active: false` when sharing is unavailable.
59
73
  *
60
74
  * @internal
61
75
  */
62
76
  export declare function useCatalogItemShareState(): ICatalogItemShareState;
63
77
  /**
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.
78
+ * Stable share actions (open/close the dialog, receive summary updates) for the
79
+ * current catalog item. Reading this instead of the state keeps a consumer from
80
+ * re-rendering on state ticks.
66
81
  *
67
82
  * @internal
68
83
  */
@@ -1 +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,2CA4DhC;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,IAAI,sBAAsB,CAEjE;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,IAAI,wBAAwB,CAErE"}
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;AAG1E,OAAO,EACH,KAAK,oBAAoB,EAG5B,MAAM,sBAAsB,CAAC;AAE9B,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;;;OAGG;IACH,OAAO,EAAE,oBAAoB,GAAG,SAAS,CAAC;IAC1C;;;;;OAKG;IACH,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,wBAAwB,GAAG,SAAS,CAAC;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,MAAM,EAAE,OAAO,CAAC;IAChB,yDAAyD;IACzD,MAAM,EAAE,gBAAgB,CAAC;CAC5B;AAED;;;GAGG;AACH,UAAU,wBAAwB;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,kGAAkG;IAClG,eAAe,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,CAAC;CAC5D;AAyBD;;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;;;;;;;;;;;;GAYG;AACH,wBAAgB,wBAAwB,CAAC,EACrC,aAAa,EACb,MAAM,EACN,MAAM,EACN,QAAQ,EACX,EAAE,8BAA8B,2CA2FhC;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,IAAI,sBAAsB,CAEjE;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,IAAI,wBAAwB,CAErE"}
@@ -1,79 +1,111 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  // (C) 2026 GoodData Corporation
3
3
  import { createContext, useCallback, useContext, useMemo, useState } from "react";
4
- import { useObjectShare } from "@gooddata/sdk-ui-ext";
4
+ import { useBackendStrict, useCancelablePromise, useWorkspaceStrict } from "@gooddata/sdk-ui";
5
+ import { accessListToSummary, isPermissionsNotAvailable, } from "@gooddata/sdk-ui-ext";
6
+ const NO_LABELS = { labels: [], loading: false, error: false };
5
7
  const INACTIVE_STATE = {
6
8
  active: false,
7
- controller: undefined,
9
+ summary: undefined,
10
+ summaryError: false,
8
11
  target: undefined,
9
12
  objectTitle: "",
10
13
  isOpen: false,
11
- labelsLoading: false,
14
+ labels: NO_LABELS,
12
15
  };
13
16
  const noop = () => { };
14
- const INACTIVE_ACTIONS = { active: false, open: noop, close: noop };
17
+ const INACTIVE_ACTIONS = {
18
+ active: false,
19
+ open: noop,
20
+ close: noop,
21
+ onSummaryChange: noop,
22
+ };
15
23
  const ShareStateContext = createContext(INACTIVE_STATE);
16
24
  const ShareActionsContext = createContext(INACTIVE_ACTIONS);
17
25
  /**
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
- * subtreeheader, tabs, status does not re-render as access is edited.
26
+ * Owns the PAGE state of sharing for one catalog item: the initial access-summary
27
+ * fetch (the inline access row is visible before the dialog ever opens, so the page
28
+ * fetches its own summary) and the dialog-open flag. The dialog itself is a separate,
29
+ * session-scoped component mounted only while open, owning its own controller and
30
+ * fetchthat reports summary changes back through `onSummaryChange`, so the row
31
+ * stays in sync with edits without a refetch.
23
32
  *
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`.
33
+ * No-ops cleanly when the item can't be shared: the contexts then carry an inactive
34
+ * value and the hooks below report `active: false`.
26
35
  *
27
36
  * @internal
28
37
  */
29
38
  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 [isOpen, setIsOpen] = useState(false);
34
- const controller = useObjectShare(target, {
35
- labels: labels.labels,
36
- labelsError: labels.error,
37
- labelsLoading: labels.loading,
38
- isOpen,
39
- });
40
- // The detail view is reused as the user navigates between objects, so close an
41
- // open dialog when the shareable target changes (or becomes non-shareable) —
42
- // otherwise it would linger open on the next object. Reset during render (React's
43
- // "adjust state on prop change" idiom) rather than in an effect.
44
- const targetKey = shareableItem?.identifier;
45
- const [openForKey, setOpenForKey] = useState(undefined);
46
- if (isOpen && openForKey !== targetKey) {
47
- setIsOpen(false);
39
+ const backend = useBackendStrict();
40
+ const workspace = useWorkspaceStrict();
41
+ // The page-session boundary: everything below (summary, open flag, the fetch) is
42
+ // scoped to this key. Workspace and kind are part of it — the same identifier can
43
+ // reappear under another workspace (or as another object kind), and the previous
44
+ // summary/dialog must not survive into it.
45
+ const itemKey = shareableItem
46
+ ? `${workspace}:${shareableItem.type}:${shareableItem.identifier}`
47
+ : undefined;
48
+ const [summary, setSummary] = useState(undefined);
49
+ // Which item's dialog is open. Navigating to another item changes `itemKey`, so
50
+ // `isOpen` turns false and the dialog unmounts.
51
+ const [dialogFor, setDialogFor] = useState(undefined);
52
+ // Both the summary and the open flag belong to one item under one backend —
53
+ // drop them when either changes. Item change: the new item shows its loading
54
+ // skeleton instead of the previous item's access, and navigating BACK to an
55
+ // item whose dialog was open must not reopen it (a lingering `dialogFor`
56
+ // would match the returning key). Backend change (compared by reference —
57
+ // an instance has no string identity to fold into `itemKey`): the previous
58
+ // backend's summary must not survive into the new one, and clearing
59
+ // `dialogFor` unmounts the dialog session, discarding its edit overlay and
60
+ // staged state with it.
61
+ const [seenScope, setSeenScope] = useState({ backend, itemKey });
62
+ if (seenScope.backend !== backend || seenScope.itemKey !== itemKey) {
63
+ setSeenScope({ backend, itemKey });
64
+ setSummary(undefined);
65
+ setDialogFor(undefined);
48
66
  }
49
- const open = useCallback(() => {
50
- setOpenForKey(targetKey);
51
- setIsOpen(true);
52
- }, [targetKey]);
53
- const close = useCallback(() => setIsOpen(false), []);
67
+ // The page-level summary fetch runs only while the summary is UNKNOWN: once
68
+ // anything seeds it — this fetch, or the dialog's `onSummaryChange` report (the
69
+ // dialog session fetches on its own) — the promise clears, so an open dialog
70
+ // never runs alongside a page fetch and a close never refetches. The item-change
71
+ // reset above clears the summary, which re-arms the fetch. The `prev ??` seed
72
+ // guard covers the resolve-vs-cancel race.
73
+ const summaryUnknown = summary === undefined;
74
+ const { status, error } = useCancelablePromise({
75
+ promise: target && summaryUnknown
76
+ ? () => backend.workspace(workspace).objectPermissions().getAccessList(target)
77
+ : undefined,
78
+ onSuccess: (list) => setSummary((prev) => prev ?? accessListToSummary(list)),
79
+ onError: () => { },
80
+ }, [backend, workspace, itemKey, summaryUnknown]);
54
81
  // Sharing is offered only while the access list is reachable. The permissions
55
82
  // endpoint is manage-gated, so a user who can only view/analyze the object gets
56
- // a 404 (surfaced as `accessUnavailable`); we then hide both the Share button and
57
- // the inline access row there is nothing they can act on, and the row would
58
- // otherwise load forever. A transient load error does not set the flag, so a flaky
59
- // fetch doesn't strip the UI. The access list loads optimistically, so the row and
60
- // button show until the 404 lands, then disappear.
61
- const active = Boolean(shareableItem) && !controller.state.accessUnavailable;
83
+ // a 404; we then hide both the Share button and the inline access row — there is
84
+ // nothing they can act on. A transient load error does not set the flag, so a
85
+ // flaky fetch doesn't strip the UI it is reported as `summaryError` instead,
86
+ // so the access row can show an error rather than load forever.
87
+ const accessUnavailable = status === "error" && isPermissionsNotAvailable(error);
88
+ const active = Boolean(shareableItem) && !accessUnavailable;
89
+ const summaryError = status === "error" && !accessUnavailable;
90
+ const isOpen = dialogFor !== undefined && dialogFor === itemKey;
91
+ const open = useCallback(() => setDialogFor(itemKey), [itemKey]);
92
+ const close = useCallback(() => setDialogFor(undefined), []);
62
93
  const state = useMemo(() => active && shareableItem
63
94
  ? {
64
95
  active: true,
65
- controller,
96
+ summary,
97
+ summaryError,
66
98
  target,
67
99
  objectTitle: shareableItem.title,
68
100
  isOpen,
69
- labelsLoading: labels.loading,
101
+ labels,
70
102
  }
71
- : INACTIVE_STATE, [active, shareableItem, controller, target, isOpen, labels.loading]);
72
- const actions = useMemo(() => (active ? { active: true, open, close } : INACTIVE_ACTIONS), [active, open, close]);
103
+ : INACTIVE_STATE, [active, shareableItem, summary, summaryError, target, isOpen, labels]);
104
+ const actions = useMemo(() => (active ? { active: true, open, close, onSummaryChange: setSummary } : INACTIVE_ACTIONS), [active, open, close]);
73
105
  return (_jsx(ShareActionsContext.Provider, { value: actions, children: _jsx(ShareStateContext.Provider, { value: state, children: children }) }));
74
106
  }
75
107
  /**
76
- * Live share state (controller, summary, open flag) for the current catalog item.
108
+ * Page share state (summary, open flag, dialog inputs) for the current catalog item.
77
109
  * Reports `active: false` when sharing is unavailable.
78
110
  *
79
111
  * @internal
@@ -82,8 +114,9 @@ export function useCatalogItemShareState() {
82
114
  return useContext(ShareStateContext);
83
115
  }
84
116
  /**
85
- * Stable share actions (open/close the dialog) for the current catalog item.
86
- * Reading this instead of the state keeps a consumer from re-rendering on state ticks.
117
+ * Stable share actions (open/close the dialog, receive summary updates) for the
118
+ * current catalog item. Reading this instead of the state keeps a consumer from
119
+ * re-rendering on state ticks.
87
120
  *
88
121
  * @internal
89
122
  */
@@ -10,6 +10,9 @@ export declare const shareMessages: {
10
10
  accessRowLabel: {
11
11
  id: string;
12
12
  };
13
+ accessRowError: {
14
+ id: string;
15
+ };
13
16
  accessRowRestricted: {
14
17
  id: string;
15
18
  };
@@ -19,6 +22,9 @@ export declare const shareMessages: {
19
22
  accessRowWorkspaceShare: {
20
23
  id: string;
21
24
  };
25
+ accessRowWorkspaceEdit: {
26
+ id: string;
27
+ };
22
28
  accessRowSharedWith: {
23
29
  id: string;
24
30
  };
@@ -1 +1 @@
1
- {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../../src/catalogDetail/share/messages.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;CAOxB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;CAKzB,CAAC"}
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../../src/catalogDetail/share/messages.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;CASxB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;CAKzB,CAAC"}
@@ -8,9 +8,11 @@ import { defineMessages } from "react-intl";
8
8
  export const shareMessages = defineMessages({
9
9
  shareButton: { id: "analyticsCatalog.share.button" },
10
10
  accessRowLabel: { id: "analyticsCatalog.share.access.row.label" },
11
+ accessRowError: { id: "analyticsCatalog.share.access.row.error" },
11
12
  accessRowRestricted: { id: "analyticsCatalog.share.access.row.restricted" },
12
13
  accessRowWorkspaceView: { id: "analyticsCatalog.share.access.row.workspace.view" },
13
14
  accessRowWorkspaceShare: { id: "analyticsCatalog.share.access.row.workspace.share" },
15
+ accessRowWorkspaceEdit: { id: "analyticsCatalog.share.access.row.workspace.edit" },
14
16
  accessRowSharedWith: { id: "analyticsCatalog.share.access.row.sharedWith" },
15
17
  });
16
18
  /**
@@ -1,4 +1,4 @@
1
- import type { IObjectShareLabel } from "@gooddata/sdk-ui-ext";
1
+ import { type IObjectShareLabel } from "@gooddata/sdk-ui-ext";
2
2
  import type { ShareableCatalogItem } from "./types.js";
3
3
  /**
4
4
  * @internal
@@ -1 +1 @@
1
- {"version":3,"file":"useShareableLabels.d.ts","sourceRoot":"","sources":["../../../src/catalogDetail/share/useShareableLabels.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAE9D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC7B,8EAA8E;IAC9E,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,KAAK,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,oBAAoB,GAAG,SAAS,GAAG,gBAAgB,CAsC3F"}
1
+ {"version":3,"file":"useShareableLabels.d.ts","sourceRoot":"","sources":["../../../src/catalogDetail/share/useShareableLabels.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,iBAAiB,EAAuB,MAAM,sBAAsB,CAAC;AAEnF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC7B,8EAA8E;IAC9E,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;OAKG;IACH,KAAK,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,oBAAoB,GAAG,SAAS,GAAG,gBAAgB,CA2C3F"}
@@ -2,6 +2,7 @@
2
2
  import { useMemo } from "react";
3
3
  import { objRefToString } from "@gooddata/sdk-model";
4
4
  import { useBackendStrict, useCancelablePromise, useWorkspaceStrict } from "@gooddata/sdk-ui";
5
+ import { sortShareableLabels } from "@gooddata/sdk-ui-ext";
5
6
  /**
6
7
  * Loads the labels (display forms) of a shareable attribute so the share dialog
7
8
  * can scope a grantee's access per label. Facts and measures have no labels —
@@ -23,13 +24,17 @@ export function useShareableLabels(item) {
23
24
  .getAttribute({ identifier: attributeRef, type: "attribute" })
24
25
  : undefined,
25
26
  }, [backend, workspace, attributeRef]);
26
- const labels = useMemo(() => (result?.displayForms ?? []).map((df) => ({
27
+ const labels = useMemo(() =>
28
+ // Sort once at the source so the detail-page labels popup and the share
29
+ // dialog's label-access checklist — both fed from here — share one
30
+ // deterministic order (primary first, then alphabetical).
31
+ sortShareableLabels((result?.displayForms ?? []).map((df) => ({
27
32
  ref: df.ref,
28
33
  id: objRefToString(df.ref),
29
34
  title: df.title,
30
35
  isPrimary: df.isPrimary === true,
31
36
  isDefault: df.isDefault === true,
32
- })), [result]);
37
+ }))), [result]);
33
38
  // Facts and measures have no fetch, so they never "load". An attribute loads
34
39
  // until its fetch settles (success or error).
35
40
  const loading = attributeRef !== undefined && status !== "success" && status !== "error";