@gooddata/sdk-ui-ext 11.43.0-alpha.2 → 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/NOTICE +4826 -13873
- package/esm/index.d.ts +2 -2
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +1 -1
- package/esm/internal/components/configurationControls/ConfigSection.d.ts +2 -1
- package/esm/internal/components/configurationControls/ConfigSection.d.ts.map +1 -1
- package/esm/internal/components/configurationControls/ConfigSection.js +7 -3
- package/esm/internal/components/configurationControls/colors/colorDropdown/ColorDropdown.d.ts +4 -0
- package/esm/internal/components/configurationControls/colors/colorDropdown/ColorDropdown.d.ts.map +1 -1
- package/esm/internal/components/configurationControls/colors/colorDropdown/ColorDropdown.js +13 -2
- package/esm/internal/components/configurationControls/colors/colorDropdown/ColorPalette.d.ts +5 -0
- package/esm/internal/components/configurationControls/colors/colorDropdown/ColorPalette.d.ts.map +1 -1
- package/esm/internal/components/configurationControls/colors/colorDropdown/ColorPalette.js +5 -3
- package/esm/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingDialog.d.ts +13 -0
- package/esm/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingDialog.d.ts.map +1 -0
- package/esm/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingDialog.js +158 -0
- package/esm/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingSection.d.ts +12 -0
- package/esm/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingSection.d.ts.map +1 -0
- package/esm/internal/components/configurationControls/conditionalFormatting/ConditionalFormattingSection.js +54 -0
- package/esm/internal/components/configurationControls/conditionalFormatting/conditionalFormattingColors.d.ts +14 -0
- package/esm/internal/components/configurationControls/conditionalFormatting/conditionalFormattingColors.d.ts.map +1 -0
- package/esm/internal/components/configurationControls/conditionalFormatting/conditionalFormattingColors.js +32 -0
- package/esm/internal/components/configurationControls/conditionalFormatting/conditionalFormattingModel.d.ts +28 -0
- package/esm/internal/components/configurationControls/conditionalFormatting/conditionalFormattingModel.d.ts.map +1 -0
- package/esm/internal/components/configurationControls/conditionalFormatting/conditionalFormattingModel.js +149 -0
- package/esm/internal/components/configurationPanels/ConfigurationPanelContent.d.ts +1 -0
- package/esm/internal/components/configurationPanels/ConfigurationPanelContent.d.ts.map +1 -1
- package/esm/internal/components/configurationPanels/PivotTableConfigurationPanel.d.ts +1 -0
- package/esm/internal/components/configurationPanels/PivotTableConfigurationPanel.d.ts.map +1 -1
- package/esm/internal/components/configurationPanels/PivotTableConfigurationPanel.js +9 -1
- package/esm/internal/components/dialogs/userManagementDialogs/AddWorkspaceToSubjects.d.ts +0 -1
- package/esm/internal/components/dialogs/userManagementDialogs/AddWorkspaceToSubjects.d.ts.map +1 -1
- package/esm/internal/components/dialogs/userManagementDialogs/AddWorkspaceToSubjects.js +3 -3
- package/esm/internal/components/dialogs/userManagementDialogs/UserEditDialog.d.ts +0 -1
- package/esm/internal/components/dialogs/userManagementDialogs/UserEditDialog.d.ts.map +1 -1
- package/esm/internal/components/dialogs/userManagementDialogs/UserEditDialog.js +2 -2
- package/esm/internal/components/dialogs/userManagementDialogs/UserGroupEditDialog.d.ts +0 -1
- package/esm/internal/components/dialogs/userManagementDialogs/UserGroupEditDialog.d.ts.map +1 -1
- package/esm/internal/components/dialogs/userManagementDialogs/UserGroupEditDialog.js +3 -3
- package/esm/internal/components/dialogs/userManagementDialogs/Workspace/AddWorkspace.d.ts +1 -2
- package/esm/internal/components/dialogs/userManagementDialogs/Workspace/AddWorkspace.d.ts.map +1 -1
- package/esm/internal/components/dialogs/userManagementDialogs/Workspace/AddWorkspace.js +2 -2
- package/esm/internal/components/dialogs/userManagementDialogs/Workspace/WorkspaceItem/GranularPermissions.d.ts +1 -2
- package/esm/internal/components/dialogs/userManagementDialogs/Workspace/WorkspaceItem/GranularPermissions.d.ts.map +1 -1
- package/esm/internal/components/dialogs/userManagementDialogs/Workspace/WorkspaceItem/GranularPermissions.js +3 -3
- package/esm/internal/components/dialogs/userManagementDialogs/Workspace/WorkspaceItem/PermissionsDropdown.d.ts +0 -1
- package/esm/internal/components/dialogs/userManagementDialogs/Workspace/WorkspaceItem/PermissionsDropdown.d.ts.map +1 -1
- package/esm/internal/components/dialogs/userManagementDialogs/Workspace/WorkspaceItem/PermissionsDropdown.js +3 -5
- package/esm/internal/components/dialogs/userManagementDialogs/Workspace/WorkspaceItem/WorkspaceItem.d.ts +1 -2
- package/esm/internal/components/dialogs/userManagementDialogs/Workspace/WorkspaceItem/WorkspaceItem.d.ts.map +1 -1
- package/esm/internal/components/dialogs/userManagementDialogs/Workspace/WorkspaceItem/WorkspaceItem.js +2 -2
- package/esm/internal/components/dialogs/userManagementDialogs/Workspace/WorkspaceList.d.ts +0 -1
- package/esm/internal/components/dialogs/userManagementDialogs/Workspace/WorkspaceList.d.ts.map +1 -1
- package/esm/internal/components/pluggableVisualizations/pivotTableNext/PluggablePivotTableNext.d.ts +3 -0
- package/esm/internal/components/pluggableVisualizations/pivotTableNext/PluggablePivotTableNext.d.ts.map +1 -1
- package/esm/internal/components/pluggableVisualizations/pivotTableNext/PluggablePivotTableNext.js +33 -5
- package/esm/internal/constants/supportedProperties.d.ts.map +1 -1
- package/esm/internal/constants/supportedProperties.js +1 -0
- package/esm/internal/translations/de-DE.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/de-DE.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/de-DE.localization-bundle.js +12 -1
- package/esm/internal/translations/en-AU.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/en-AU.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/en-AU.localization-bundle.js +12 -1
- package/esm/internal/translations/en-GB.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/en-GB.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/en-GB.localization-bundle.js +12 -1
- package/esm/internal/translations/en-US.localization-bundle.d.ts +168 -0
- package/esm/internal/translations/en-US.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/en-US.localization-bundle.js +168 -0
- package/esm/internal/translations/es-419.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/es-419.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/es-419.localization-bundle.js +12 -1
- package/esm/internal/translations/es-ES.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/es-ES.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/es-ES.localization-bundle.js +12 -1
- package/esm/internal/translations/fi-FI.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/fi-FI.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/fi-FI.localization-bundle.js +12 -1
- package/esm/internal/translations/fr-CA.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/fr-CA.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/fr-CA.localization-bundle.js +12 -1
- package/esm/internal/translations/fr-FR.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/fr-FR.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/fr-FR.localization-bundle.js +12 -1
- package/esm/internal/translations/id-ID.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/id-ID.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/id-ID.localization-bundle.js +12 -1
- package/esm/internal/translations/it-IT.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/it-IT.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/it-IT.localization-bundle.js +12 -1
- package/esm/internal/translations/ja-JP.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/ja-JP.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/ja-JP.localization-bundle.js +12 -1
- package/esm/internal/translations/ko-KR.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/ko-KR.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/ko-KR.localization-bundle.js +12 -1
- package/esm/internal/translations/nl-NL.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/nl-NL.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/nl-NL.localization-bundle.js +12 -1
- package/esm/internal/translations/pl-PL.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/pl-PL.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/pl-PL.localization-bundle.js +12 -1
- package/esm/internal/translations/pt-BR.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/pt-BR.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/pt-BR.localization-bundle.js +12 -1
- package/esm/internal/translations/pt-PT.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/pt-PT.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/pt-PT.localization-bundle.js +12 -1
- package/esm/internal/translations/ru-RU.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/ru-RU.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/ru-RU.localization-bundle.js +12 -1
- package/esm/internal/translations/sl-SI.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/sl-SI.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/sl-SI.localization-bundle.js +12 -1
- package/esm/internal/translations/th-TH.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/th-TH.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/th-TH.localization-bundle.js +12 -1
- package/esm/internal/translations/tr-TR.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/tr-TR.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/tr-TR.localization-bundle.js +12 -1
- package/esm/internal/translations/uk-UA.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/uk-UA.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/uk-UA.localization-bundle.js +12 -1
- package/esm/internal/translations/vi-VN.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/vi-VN.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/vi-VN.localization-bundle.js +12 -1
- package/esm/internal/translations/zh-HK.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/zh-HK.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/zh-HK.localization-bundle.js +12 -1
- package/esm/internal/translations/zh-Hans.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/zh-Hans.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/zh-Hans.localization-bundle.js +12 -1
- package/esm/internal/translations/zh-Hant.localization-bundle.d.ts +11 -0
- package/esm/internal/translations/zh-Hant.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/zh-Hant.localization-bundle.js +12 -1
- package/esm/internal/utils/propertiesHelper.d.ts +2 -1
- package/esm/internal/utils/propertiesHelper.d.ts.map +1 -1
- package/esm/internal/utils/propertiesHelper.js +3 -0
- package/esm/locales.d.ts +79 -0
- package/esm/locales.d.ts.map +1 -1
- package/esm/locales.js +46 -0
- package/esm/sdk-ui-ext.d.ts +24 -10
- package/esm/share/ObjectShareDialog.js +2 -2
- package/esm/share/objectShareController.helpers.d.ts +0 -26
- package/esm/share/objectShareController.helpers.d.ts.map +1 -1
- package/esm/share/objectShareController.helpers.js +2 -38
- package/esm/share/objectShareController.types.d.ts +39 -0
- package/esm/share/objectShareController.types.d.ts.map +1 -1
- package/esm/share/useAccessList.d.ts +31 -18
- package/esm/share/useAccessList.d.ts.map +1 -1
- package/esm/share/useAccessList.js +85 -133
- package/esm/share/useLabelScope.d.ts +6 -8
- package/esm/share/useLabelScope.d.ts.map +1 -1
- package/esm/share/useLabelScope.js +34 -28
- package/esm/share/useObjectShareController.d.ts +19 -16
- package/esm/share/useObjectShareController.d.ts.map +1 -1
- package/esm/share/useObjectShareController.js +212 -119
- package/package.json +21 -21
- package/styles/css/main.css +238 -0
- package/styles/css/main.css.map +1 -1
- package/styles/internal/css/conditional_formatting.css +239 -0
- package/styles/internal/css/conditional_formatting.css.map +1 -0
- package/styles/internal/css/config_panel.css +5 -0
- package/styles/internal/css/config_panel.css.map +1 -1
- package/styles/internal/scss/conditional_formatting.scss +269 -0
- package/styles/internal/scss/config_panel.scss +11 -0
- package/styles/scss/main.scss +1 -0
- package/esm/share/useObjectShare.d.ts +0 -50
- package/esm/share/useObjectShare.d.ts.map +0 -1
- package/esm/share/useObjectShare.js +0 -17
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
// (C) 2026 GoodData Corporation
|
|
2
|
-
import { useCallback, useEffect, useMemo,
|
|
3
|
-
import { objRefToString
|
|
2
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { objRefToString } from "@gooddata/sdk-model";
|
|
4
4
|
import { useBackendStrict, useCancelablePromise, useWorkspaceStrict } from "@gooddata/sdk-ui";
|
|
5
5
|
import { useToastMessage } from "@gooddata/sdk-ui-kit";
|
|
6
6
|
import { deriveGeneralAccess, deriveWorkspacePermissionLevel } from "./accessSummary.js";
|
|
7
7
|
import { objectShareMessages } from "./messages.js";
|
|
8
|
-
import { assigneeMatchesQuery, granteeId, granteesFromAccessList
|
|
8
|
+
import { assigneeMatchesQuery, granteeId, granteesFromAccessList } from "./objectShareController.helpers.js";
|
|
9
9
|
/**
|
|
10
|
-
* Owns the backend access list
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
10
|
+
* Owns the backend access list. It is fetched once (per target) then seeded into
|
|
11
|
+
* local state, which is authoritative while the dialog/summary is mounted:
|
|
12
|
+
* mutations write through to it directly and roll back the one changed entry on
|
|
13
|
+
* failure. There is no post-write refetch, so the grantee list never blanks and
|
|
14
|
+
* never fights the backend's read-after-write lag. A target switch re-seeds from
|
|
15
|
+
* the new fetch; the seed is gated on a per-target stamp so a late fetch for a
|
|
16
|
+
* previous target can't clobber the current one.
|
|
17
|
+
*
|
|
18
|
+
* Effective (inherited) permissions and display names come from that single
|
|
19
|
+
* fetch: inherited access doesn't change as a result of editing a direct grant,
|
|
20
|
+
* so the badge stays correct for the session without re-reading.
|
|
15
21
|
*
|
|
16
22
|
* @internal
|
|
17
23
|
*/
|
|
@@ -19,37 +25,24 @@ export function useAccessList(target, onSaved) {
|
|
|
19
25
|
const backend = useBackendStrict();
|
|
20
26
|
const workspace = useWorkspaceStrict();
|
|
21
27
|
const toast = useToastMessage();
|
|
22
|
-
|
|
23
|
-
const [
|
|
24
|
-
const [
|
|
28
|
+
// Authoritative local state, seeded from the fetch below.
|
|
29
|
+
const [grantees, setGrantees] = useState([]);
|
|
30
|
+
const [generalAccess, setGeneralAccess] = useState("RESTRICTED");
|
|
31
|
+
const [workspaceLevel, setWorkspaceLevel] = useState("VIEW");
|
|
32
|
+
// The target the local state was seeded for; undefined until the first seed.
|
|
33
|
+
// Reading state as belonging to the current target hinges on this matching
|
|
34
|
+
// `targetKey` — a target switch makes the old state stale until the re-seed.
|
|
35
|
+
const [seededTarget, setSeededTarget] = useState(undefined);
|
|
25
36
|
const [loadError, setLoadError] = useState(undefined);
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
// flight. The backend has read-after-write lag, so we show the intended state
|
|
29
|
-
// and clear an entry only once a refresh confirms it (or the write fails).
|
|
30
|
-
const [overlay, setOverlay] = useState({});
|
|
31
|
-
// Display names learned from the picker; keep a human name on a row after the
|
|
32
|
-
// optimistic overlay reconciles away (the grant often returns only a raw id).
|
|
37
|
+
// Display names learned from the picker; used to give a fetched row a human
|
|
38
|
+
// name when its grant carried only a raw id.
|
|
33
39
|
const [knownNames, setKnownNames] = useState({});
|
|
34
40
|
// Original ObjRef per grantee id, learned from the picker (the access-list id
|
|
35
41
|
// is a serialized `kind:identifier`, which loses Uri-vs-Id). Reused for writes.
|
|
36
42
|
const [knownRefs, setKnownRefs] = useState({});
|
|
37
|
-
// Optimistic general-access override — held from confirm until the fetched list
|
|
38
|
-
// reflects it, so the radio + summary update instantly.
|
|
39
|
-
const [optimisticGeneralAccess, setOptimisticGeneralAccess] = useState(undefined);
|
|
40
43
|
const targetKey = target ? objRefToString(target.ref) : undefined;
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
// means everything derived ignores it immediately — no reset effect.
|
|
44
|
-
const currentAccessList = accessListTarget === targetKey ? accessList : undefined;
|
|
45
|
-
// Always-current target key, read inside async callbacks that captured an older
|
|
46
|
-
// one. A background refresh started for the previous target can resolve after a
|
|
47
|
-
// switch; without this it would stamp the list back to the stale target, leaving
|
|
48
|
-
// the new target's `currentAccessList` undefined (and, with the load-gated
|
|
49
|
-
// status, stuck "loading" since its own fetch already settled).
|
|
50
|
-
const targetKeyRef = useRef(targetKey);
|
|
51
|
-
targetKeyRef.current = targetKey;
|
|
52
|
-
// Initial (and target-change) load. Feeds setAccessList once; thereafter we own it.
|
|
44
|
+
// Initial (and target-change) load. Seeds local state once per target; thereafter
|
|
45
|
+
// local state is authoritative and mutations write through it.
|
|
53
46
|
const { result: fetchedList, status: fetchStatus, error: fetchError, } = useCancelablePromise({
|
|
54
47
|
promise: target
|
|
55
48
|
? () => backend.workspace(workspace).objectPermissions().getAccessList(target)
|
|
@@ -58,118 +51,77 @@ export function useAccessList(target, onSaved) {
|
|
|
58
51
|
// Key on the serialized ref, not the ObjRef object — an inline idRef(...) is a
|
|
59
52
|
// new instance each render and would otherwise refetch forever.
|
|
60
53
|
[backend, workspace, target?.kind, targetKey]);
|
|
54
|
+
// Local state belongs to the current target only once its list has been seeded
|
|
55
|
+
// AND no fetch is in flight for it. Requiring `fetchStatus === "success"` (not
|
|
56
|
+
// just a matching `seededTarget`) matters when the same object is reopened after
|
|
57
|
+
// navigating away: the deps change re-runs `useCancelablePromise`, which resets to
|
|
58
|
+
// `loading` while it refetches. Without this, the still-matching previous
|
|
59
|
+
// `seededTarget` would keep `hasList` true and surface the prior session's
|
|
60
|
+
// grantees as `success` (mutations enabled) until the new list lands.
|
|
61
|
+
const hasList = seededTarget === targetKey && targetKey !== undefined && fetchStatus === "success";
|
|
61
62
|
useEffect(() => {
|
|
62
63
|
if (fetchStatus === "success" && fetchedList) {
|
|
63
64
|
// The cancelable promise is keyed on targetKey, so this list is for it.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
setGrantees(granteesFromAccessList(fetchedList));
|
|
66
|
+
setGeneralAccess(deriveGeneralAccess(fetchedList.grants));
|
|
67
|
+
setWorkspaceLevel(deriveWorkspacePermissionLevel(fetchedList.grants));
|
|
68
|
+
setSeededTarget(targetKey);
|
|
67
69
|
setLoadError(undefined);
|
|
68
|
-
setOverlay({}); // a target-change load is authoritative; drop stale overlay
|
|
69
|
-
setOptimisticGeneralAccess(undefined);
|
|
70
70
|
}
|
|
71
71
|
else if (fetchStatus === "error") {
|
|
72
|
-
setLoadStatus("error");
|
|
73
72
|
setLoadError(fetchError instanceof Error ? fetchError : new Error(String(fetchError)));
|
|
74
73
|
}
|
|
75
74
|
else if (fetchStatus === "loading") {
|
|
76
|
-
|
|
75
|
+
// A (re)fetch is in flight — including when the same object is reopened
|
|
76
|
+
// after navigating away. Drop the seed stamp so the previous session's
|
|
77
|
+
// list isn't surfaced as the current one until the fresh fetch is seeded.
|
|
78
|
+
setSeededTarget(undefined);
|
|
77
79
|
}
|
|
78
80
|
}, [fetchStatus, fetchedList, fetchError, targetKey]);
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
try {
|
|
86
|
-
const fresh = await backend.workspace(workspace).objectPermissions().getAccessList(target);
|
|
87
|
-
// The target may have changed while this refresh was in flight. Stamping
|
|
88
|
-
// a stale target's list now would clobber the new target's stamp and
|
|
89
|
-
// strand it loading, so drop the result if we've since switched away.
|
|
90
|
-
if (targetKeyRef.current !== targetKey) {
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
setAccessList(fresh);
|
|
94
|
-
setAccessListTarget(targetKey);
|
|
95
|
-
setOverlay((prev) => reconcileOverlay(granteesFromAccessList(fresh), prev));
|
|
96
|
-
// Drop the optimistic general-access override once the server confirms it.
|
|
97
|
-
const freshGeneralAccess = deriveGeneralAccess(fresh.grants);
|
|
98
|
-
setOptimisticGeneralAccess((prev) => (prev === freshGeneralAccess ? undefined : prev));
|
|
99
|
-
}
|
|
100
|
-
catch {
|
|
101
|
-
// The refresh failed, but the mutation that triggered it already
|
|
102
|
-
// succeeded. The overlay can't be reconciled against fresh data now, so
|
|
103
|
-
// settle it optimistically: keep each entry's intended state (a `set`
|
|
104
|
-
// value stays, a `remove` keeps hiding the row) but clear its `pending`
|
|
105
|
-
// flag. Otherwise rows would spin on "saving"/"removing" forever even
|
|
106
|
-
// though the change persisted and a success toast was shown.
|
|
107
|
-
setOverlay((prev) => {
|
|
108
|
-
let changed = false;
|
|
109
|
-
const next = {};
|
|
110
|
-
for (const [id, entry] of Object.entries(prev)) {
|
|
111
|
-
if (entry.pending) {
|
|
112
|
-
changed = true;
|
|
113
|
-
next[id] = { ...entry, pending: false };
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
next[id] = entry;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
return changed ? next : prev;
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
}, [backend, workspace, target, targetKey]);
|
|
123
|
-
// Committed rows overlaid with optimistic intent; backfill a human name from
|
|
124
|
-
// the picker cache where the grant only carried a raw id.
|
|
125
|
-
const grantees = useMemo(() => {
|
|
126
|
-
if (!currentAccessList) {
|
|
127
|
-
return [];
|
|
128
|
-
}
|
|
129
|
-
const committed = granteesFromAccessList(currentAccessList).map((g) => {
|
|
81
|
+
// The current target's rows, with a human name backfilled from the picker
|
|
82
|
+
// cache where the grant returned only a raw id. Empty until the current
|
|
83
|
+
// target's list is seeded, so a stale previous target's rows never show
|
|
84
|
+
// through the switch window. Derivation, not state — re-applies as the cache grows.
|
|
85
|
+
const namedGrantees = useMemo(() => hasList
|
|
86
|
+
? grantees.map((g) => {
|
|
130
87
|
const known = knownNames[g.id];
|
|
131
88
|
return known && g.name === objRefToString(g.granteeRef) ? { ...g, name: known } : g;
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
89
|
+
})
|
|
90
|
+
: [], [hasList, grantees, knownNames]);
|
|
91
|
+
// Stable sorted key of the currently-granted ids — drives the picker's
|
|
92
|
+
// "exclude already-granted" filter. Keyed on ids only (not names), so the
|
|
93
|
+
// picker's own name-cache writes can't change loadOptions' identity and
|
|
94
|
+
// re-trigger its fetch.
|
|
95
|
+
const excludedIdsKey = useMemo(() => grantees
|
|
96
|
+
.filter((g) => g.pending !== "removing")
|
|
136
97
|
.map((g) => g.id)
|
|
137
98
|
.sort()
|
|
138
|
-
.join(",");
|
|
139
|
-
const committedGeneralAccess = useMemo(() => (currentAccessList ? deriveGeneralAccess(currentAccessList.grants) : "RESTRICTED"), [currentAccessList]);
|
|
140
|
-
const generalAccess = optimisticGeneralAccess ?? committedGeneralAccess;
|
|
99
|
+
.join(","), [grantees]);
|
|
141
100
|
const summary = useMemo(() => {
|
|
142
|
-
if (!
|
|
101
|
+
if (!hasList) {
|
|
143
102
|
return undefined;
|
|
144
103
|
}
|
|
145
104
|
return {
|
|
146
105
|
generalAccess,
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
// (which can still read SHARE through read-after-write lag).
|
|
150
|
-
workspaceLevel: optimisticGeneralAccess
|
|
151
|
-
? "VIEW"
|
|
152
|
-
: deriveWorkspacePermissionLevel(currentAccessList.grants),
|
|
153
|
-
granteeCount: grantees.length,
|
|
106
|
+
workspaceLevel: generalAccess === "WORKSPACE" ? workspaceLevel : "VIEW",
|
|
107
|
+
granteeCount: namedGrantees.filter((g) => g.pending !== "removing").length,
|
|
154
108
|
};
|
|
155
|
-
}, [
|
|
156
|
-
// "success" only once the *current* target's list has
|
|
157
|
-
// target switch the
|
|
158
|
-
//
|
|
159
|
-
//
|
|
160
|
-
//
|
|
161
|
-
// row hide both summary and skeleton. Treat "no list for this target yet" as
|
|
162
|
-
// loading.
|
|
109
|
+
}, [hasList, generalAccess, workspaceLevel, namedGrantees]);
|
|
110
|
+
// "success" only once the *current* target's list has been seeded. After a
|
|
111
|
+
// target switch the previous seed is stale (seededTarget !== targetKey), so
|
|
112
|
+
// gating on hasList keeps the status at "loading" until the new list lands —
|
|
113
|
+
// never "success" with no list, which would let the dialog enable mutations
|
|
114
|
+
// and the catalog row hide both summary and skeleton.
|
|
163
115
|
const status = target
|
|
164
|
-
?
|
|
116
|
+
? fetchStatus === "error"
|
|
165
117
|
? "error"
|
|
166
|
-
:
|
|
167
|
-
? "
|
|
168
|
-
: "
|
|
118
|
+
: hasList
|
|
119
|
+
? "success"
|
|
120
|
+
: "loading"
|
|
169
121
|
: "idle";
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
//
|
|
122
|
+
// Write a single grant change to the backend, then toast. The caller applies
|
|
123
|
+
// the optimistic local write-through and rolls it back on failure; there is no
|
|
124
|
+
// refetch — local state stays authoritative.
|
|
173
125
|
const commit = useCallback(async (mutate, successMessage) => {
|
|
174
126
|
if (!target) {
|
|
175
127
|
return false;
|
|
@@ -181,18 +133,17 @@ export function useAccessList(target, onSaved) {
|
|
|
181
133
|
.manageObjectPermissions(target, mutate);
|
|
182
134
|
toast.addSuccess(successMessage);
|
|
183
135
|
onSaved?.();
|
|
184
|
-
await refresh();
|
|
185
136
|
return true;
|
|
186
137
|
}
|
|
187
138
|
catch {
|
|
188
139
|
toast.addError(objectShareMessages.toastError);
|
|
189
140
|
return false;
|
|
190
141
|
}
|
|
191
|
-
}, [backend, workspace, target, toast, onSaved
|
|
142
|
+
}, [backend, workspace, target, toast, onSaved]);
|
|
192
143
|
// Picker loader — fetches available assignees on demand, filters by the typed
|
|
193
|
-
// query, excludes anything already granted. Depends only on the
|
|
194
|
-
// (
|
|
195
|
-
//
|
|
144
|
+
// query, excludes anything already granted. Depends only on the granted set
|
|
145
|
+
// (grantees), so its identity changes only when that set actually changes. It
|
|
146
|
+
// must NOT write state that feeds its own deps.
|
|
196
147
|
const loadOptions = useCallback(async (search) => {
|
|
197
148
|
if (!target) {
|
|
198
149
|
return { groups: [], users: [] };
|
|
@@ -223,7 +174,7 @@ export function useAccessList(target, onSaved) {
|
|
|
223
174
|
}
|
|
224
175
|
return next;
|
|
225
176
|
});
|
|
226
|
-
const excluded = new Set(
|
|
177
|
+
const excluded = new Set(excludedIdsKey ? excludedIdsKey.split(",") : []);
|
|
227
178
|
const selectable = withIds
|
|
228
179
|
.filter(({ id }) => !excluded.has(id)) // hide anyone already granted
|
|
229
180
|
.filter(({ assignee }) => assigneeMatchesQuery(assignee, query));
|
|
@@ -240,24 +191,25 @@ export function useAccessList(target, onSaved) {
|
|
|
240
191
|
.filter(({ assignee }) => assignee.type !== "user")
|
|
241
192
|
.map(({ assignee, id }) => ({ id, kind: "group", name: assignee.name })),
|
|
242
193
|
};
|
|
243
|
-
}, [backend, workspace, target,
|
|
194
|
+
}, [backend, workspace, target, excludedIdsKey]);
|
|
244
195
|
// Reuse the picker's original ref (preserves UriRef vs IdentifierRef);
|
|
245
196
|
// fall back to the serialized id only if it wasn't cached.
|
|
246
197
|
const refForId = useCallback((id) => knownRefs[id] ?? { identifier: id.split(":", 2)[1] }, [knownRefs]);
|
|
247
198
|
return {
|
|
248
199
|
targetKey,
|
|
249
|
-
|
|
250
|
-
grantees,
|
|
251
|
-
granteeIdsKey,
|
|
200
|
+
hasList,
|
|
201
|
+
grantees: namedGrantees,
|
|
252
202
|
generalAccess,
|
|
203
|
+
workspaceLevel,
|
|
253
204
|
summary,
|
|
254
205
|
status,
|
|
255
206
|
loadError,
|
|
256
207
|
commit,
|
|
257
208
|
loadOptions,
|
|
258
209
|
refForId,
|
|
259
|
-
|
|
210
|
+
setGrantees,
|
|
211
|
+
setGeneralAccess,
|
|
212
|
+
setWorkspaceLevel,
|
|
260
213
|
setKnownNames,
|
|
261
|
-
setOptimisticGeneralAccess,
|
|
262
214
|
};
|
|
263
215
|
}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { type MutableRefObject } from "react";
|
|
2
1
|
import { type IObjectPermissionsObject } from "@gooddata/sdk-backend-spi";
|
|
3
|
-
import type { IObjectAccessList } from "@gooddata/sdk-model";
|
|
4
2
|
import { type LabelScopePrincipal } from "./objectShareController.helpers.js";
|
|
5
3
|
import type { IObjectShareLabel } from "./types.js";
|
|
6
4
|
/**
|
|
@@ -30,11 +28,6 @@ export interface ILabelScope {
|
|
|
30
28
|
/** Per-grantee label scope: grantee id → label ids in scope (primary always in). */
|
|
31
29
|
selectedLabelIdsByGrantee: Record<string, string[]>;
|
|
32
30
|
setSelectedLabelIdsByGrantee: React.Dispatch<React.SetStateAction<Record<string, string[]>>>;
|
|
33
|
-
/**
|
|
34
|
-
* Grantee ids whose scope was just written optimistically; the resolution
|
|
35
|
-
* effect keeps these instead of overwriting them through read-after-write lag.
|
|
36
|
-
*/
|
|
37
|
-
optimisticScopeRef: MutableRefObject<Set<string>>;
|
|
38
31
|
/**
|
|
39
32
|
* The single per-label write path. Diffs `desired` vs `current` over the
|
|
40
33
|
* permissionable labels and grants/revokes for one principal. Returns false
|
|
@@ -48,7 +41,12 @@ export interface ILabelScope {
|
|
|
48
41
|
* which labels are permissionable, and exposes one reconcile primitive shared by
|
|
49
42
|
* add / remove / general-access / the labels picker so their behavior can't drift.
|
|
50
43
|
*
|
|
44
|
+
* The resolved scope is local-authoritative: the probe seeds a scope only for
|
|
45
|
+
* grantees it doesn't already know, so an optimistic scope written for a freshly
|
|
46
|
+
* added grantee is never overwritten by the backend's lagging read. Local scopes
|
|
47
|
+
* are dropped on a target switch so the next object re-resolves from scratch.
|
|
48
|
+
*
|
|
51
49
|
* @internal
|
|
52
50
|
*/
|
|
53
|
-
export declare function useLabelScope(target: IObjectPermissionsObject | undefined, targetKey: string | undefined, labels: IObjectShareLabel[],
|
|
51
|
+
export declare function useLabelScope(target: IObjectPermissionsObject | undefined, targetKey: string | undefined, labels: IObjectShareLabel[], hasList: boolean, committedGranteeIds: string[], labelsError: boolean, labelsLoading: boolean): ILabelScope;
|
|
54
52
|
//# sourceMappingURL=useLabelScope.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLabelScope.d.ts","sourceRoot":"","sources":["../../src/share/useLabelScope.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useLabelScope.d.ts","sourceRoot":"","sources":["../../src/share/useLabelScope.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,wBAAwB,EAA6B,MAAM,2BAA2B,CAAC;AAGrG,OAAO,EACH,KAAK,mBAAmB,EAG3B,MAAM,oCAAoC,CAAC;AAC5C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAWpD;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IACxB;;;;OAIG;IACH,eAAe,EAAE,iBAAiB,EAAE,CAAC;IACrC;;;;;;;;OAQG;IACH,cAAc,EAAE,OAAO,CAAC;IACxB,sFAAoF;IACpF,yBAAyB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACpD,4BAA4B,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IAC7F;;;;OAIG;IACH,mBAAmB,EAAE,CACjB,SAAS,EAAE,mBAAmB,EAC9B,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,EACpC,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,KACnC,OAAO,CAAC,OAAO,CAAC,CAAC;CACzB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CACzB,MAAM,EAAE,wBAAwB,GAAG,SAAS,EAC5C,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,MAAM,EAAE,iBAAiB,EAAE,EAC3B,OAAO,EAAE,OAAO,EAChB,mBAAmB,EAAE,MAAM,EAAE,EAC7B,WAAW,EAAE,OAAO,EACpB,aAAa,EAAE,OAAO,GACvB,WAAW,CAsKb"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// (C) 2026 GoodData Corporation
|
|
2
|
-
import { useCallback, useEffect, useMemo,
|
|
2
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
3
3
|
import { isUnexpectedResponseError } from "@gooddata/sdk-backend-spi";
|
|
4
4
|
import { useBackendStrict, useWorkspaceStrict } from "@gooddata/sdk-ui";
|
|
5
|
-
import { buildLabelMutations,
|
|
5
|
+
import { buildLabelMutations, isGranteeGrantedIn, } from "./objectShareController.helpers.js";
|
|
6
6
|
/**
|
|
7
7
|
* Whether a label's access-list error means the label is genuinely not
|
|
8
8
|
* independently permissionable (a definitive 404 / 501), as opposed to a
|
|
@@ -17,13 +17,17 @@ function isNotPermissionable(error) {
|
|
|
17
17
|
* which labels are permissionable, and exposes one reconcile primitive shared by
|
|
18
18
|
* add / remove / general-access / the labels picker so their behavior can't drift.
|
|
19
19
|
*
|
|
20
|
+
* The resolved scope is local-authoritative: the probe seeds a scope only for
|
|
21
|
+
* grantees it doesn't already know, so an optimistic scope written for a freshly
|
|
22
|
+
* added grantee is never overwritten by the backend's lagging read. Local scopes
|
|
23
|
+
* are dropped on a target switch so the next object re-resolves from scratch.
|
|
24
|
+
*
|
|
20
25
|
* @internal
|
|
21
26
|
*/
|
|
22
|
-
export function useLabelScope(target, targetKey, labels,
|
|
27
|
+
export function useLabelScope(target, targetKey, labels, hasList, committedGranteeIds, labelsError, labelsLoading) {
|
|
23
28
|
const backend = useBackendStrict();
|
|
24
29
|
const workspace = useWorkspaceStrict();
|
|
25
30
|
const [selectedLabelIdsByGrantee, setSelectedLabelIdsByGrantee] = useState({});
|
|
26
|
-
const optimisticScopeRef = useRef(new Set());
|
|
27
31
|
// Label ids whose permissions endpoint responded — not every display form is
|
|
28
32
|
// independently permissionable (some 404). `undefined` means "not resolved
|
|
29
33
|
// yet" (assume all).
|
|
@@ -39,19 +43,30 @@ export function useLabelScope(target, targetKey, labels, currentAccessList, gran
|
|
|
39
43
|
useEffect(() => {
|
|
40
44
|
setPermissionableLabelIds(undefined);
|
|
41
45
|
}, [targetKey, labelsKey]);
|
|
46
|
+
// A target switch — or a label-set change under the same target (labels finish
|
|
47
|
+
// loading, a label added/removed) — drops every resolved scope so the seeding
|
|
48
|
+
// effect below re-resolves from the current label set's per-label lists rather
|
|
49
|
+
// than preserving scopes computed against the old labels. Without the labelsKey
|
|
50
|
+
// reset, an existing grantee would keep a scope missing a newly-added label even
|
|
51
|
+
// when the backend grants them access to it. (An add under the SAME labels only
|
|
52
|
+
// changes granteeIdsKey, not labelsKey, so an optimistic scope still survives.)
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
setSelectedLabelIdsByGrantee({});
|
|
55
|
+
}, [targetKey, labelsKey]);
|
|
56
|
+
// Stable string key of the committed grantee ids — the array is rebuilt each
|
|
57
|
+
// render, so the effect keys on this instead to re-resolve only on a real change.
|
|
58
|
+
const granteeIdsKey = committedGranteeIds.slice().sort().join(",");
|
|
42
59
|
// Resolve each grantee's label scope: fetch every label's access list once and
|
|
43
60
|
// record, per grantee, which labels they appear in (primary label always counts).
|
|
44
61
|
// Keyed on the committed grantee ids + labels so it re-resolves after add/remove.
|
|
45
|
-
// `
|
|
62
|
+
// `hasList` is a dep too: a list that loads with no named grantees keeps
|
|
46
63
|
// `granteeIdsKey` empty, so without it the effect would never run and the
|
|
47
64
|
// permissionable set (404 filtering) would never resolve.
|
|
48
|
-
const hasAccessList = currentAccessList !== undefined;
|
|
49
65
|
useEffect(() => {
|
|
50
|
-
if (!target || labels.length === 0 || !
|
|
66
|
+
if (!target || labels.length === 0 || !hasList) {
|
|
51
67
|
return;
|
|
52
68
|
}
|
|
53
69
|
let cancelled = false;
|
|
54
|
-
const committedGranteeIds = granteesFromAccessList(currentAccessList).map((g) => g.id);
|
|
55
70
|
Promise.all(labels.map((label) => backend
|
|
56
71
|
.workspace(workspace)
|
|
57
72
|
.objectPermissions()
|
|
@@ -89,13 +104,14 @@ export function useLabelScope(target, targetKey, labels, currentAccessList, gran
|
|
|
89
104
|
}
|
|
90
105
|
}
|
|
91
106
|
setSelectedLabelIdsByGrantee((prev) => {
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
107
|
+
// Seed a scope only for grantees we don't already have one for. A
|
|
108
|
+
// scope written optimistically (a fresh add, a labels edit) is
|
|
109
|
+
// local-authoritative and must survive the re-resolution that the
|
|
110
|
+
// grantee-set change triggers — the backend's lagging read would
|
|
111
|
+
// otherwise reset it. Grantees gone from the list are dropped.
|
|
112
|
+
const next = {};
|
|
113
|
+
for (const id of committedGranteeIds) {
|
|
114
|
+
next[id] = prev[id] ?? resolved[id];
|
|
99
115
|
}
|
|
100
116
|
return next;
|
|
101
117
|
});
|
|
@@ -105,23 +121,14 @@ export function useLabelScope(target, targetKey, labels, currentAccessList, gran
|
|
|
105
121
|
cancelled = true;
|
|
106
122
|
};
|
|
107
123
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
108
|
-
}, [backend, workspace, targetKey, labelsKey, granteeIdsKey,
|
|
109
|
-
// A label-set change OR a target switch re-resolves scopes; drop the optimistic-
|
|
110
|
-
// scope guard so the freshly-resolved scopes aren't held stale. Keying on
|
|
111
|
-
// labelsKey alone leaked across objects that happen to share a labelsKey (e.g.
|
|
112
|
-
// during navigation, before the new object's labels load): the prior object's
|
|
113
|
-
// optimistic entries would then merge over the new object's resolved scopes and
|
|
114
|
-
// show/save the wrong label grants. targetKey makes the guard per-object.
|
|
115
|
-
useEffect(() => {
|
|
116
|
-
optimisticScopeRef.current = new Set();
|
|
117
|
-
}, [labelsKey, targetKey]);
|
|
124
|
+
}, [backend, workspace, targetKey, labelsKey, granteeIdsKey, hasList]);
|
|
118
125
|
// Only labels whose permissions endpoint responded are scope-controllable; until
|
|
119
126
|
// resolution completes (permissionableLabelIds undefined) assume all are usable.
|
|
120
127
|
// While the current target's list isn't loaded yet, ignore a permissionable set
|
|
121
128
|
// left over from a previous object so we never mis-filter the new labels.
|
|
122
|
-
const effectiveLabels = useMemo(() =>
|
|
129
|
+
const effectiveLabels = useMemo(() => hasList && permissionableLabelIds
|
|
123
130
|
? labels.filter((l) => permissionableLabelIds.has(l.id))
|
|
124
|
-
: labels, [labels, permissionableLabelIds,
|
|
131
|
+
: labels, [labels, permissionableLabelIds, hasList]);
|
|
125
132
|
// Resolution is done when the probe has produced a permissionable set, or when
|
|
126
133
|
// there are genuinely no labels to probe (a label-free object). Until then
|
|
127
134
|
// callers must treat the scope as unknown (not "all selected").
|
|
@@ -154,7 +161,6 @@ export function useLabelScope(target, targetKey, labels, currentAccessList, gran
|
|
|
154
161
|
labelsResolved,
|
|
155
162
|
selectedLabelIdsByGrantee,
|
|
156
163
|
setSelectedLabelIdsByGrantee,
|
|
157
|
-
optimisticScopeRef,
|
|
158
164
|
reconcileLabelScope,
|
|
159
165
|
};
|
|
160
166
|
}
|
|
@@ -1,24 +1,27 @@
|
|
|
1
1
|
import type { IObjectPermissionsObject } from "@gooddata/sdk-backend-spi";
|
|
2
|
-
import { type IObjectShareController } from "./objectShareController.types.js";
|
|
3
|
-
import type { IObjectShareLabel } from "./types.js";
|
|
2
|
+
import { type IObjectShareController, type IUseObjectShareOptions } from "./objectShareController.types.js";
|
|
4
3
|
/**
|
|
5
|
-
* Manages the share dialog state and backend I/O for a single shareable
|
|
6
|
-
* object.
|
|
4
|
+
* Manages the share dialog state and backend I/O for a single shareable object.
|
|
7
5
|
*
|
|
8
|
-
* The access list is fetched
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* concern (see {@link ObjectShareDialog}).
|
|
6
|
+
* The access list is fetched once and seeded into local state, which is the
|
|
7
|
+
* source of truth while the dialog/summary is mounted. Each access change is
|
|
8
|
+
* written through to local state immediately and sent to the backend; on failure
|
|
9
|
+
* the one changed entry is rolled back. There is no post-write refetch, so the
|
|
10
|
+
* grantee list never blanks and never fights the backend's read-after-write lag.
|
|
11
|
+
* Top-level open/close is the consumer's concern (see {@link ObjectShareDialog}).
|
|
15
12
|
*
|
|
16
|
-
* Mutations follow a **commit-on-interaction** model: each access change is
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
13
|
+
* Mutations follow a **commit-on-interaction** model: each access change is sent
|
|
14
|
+
* immediately; the general-access toggle goes through a confirm step because it
|
|
15
|
+
* is high-impact. There is no batched Save. The list is fetched eagerly so
|
|
16
|
+
* `state.summary` also drives an inline access row while closed.
|
|
17
|
+
*
|
|
18
|
+
* Most consumers do not call this directly — render {@link ObjectShareDialog} with
|
|
19
|
+
* plain props and it owns its controller. Call this (exported as `useObjectShare`)
|
|
20
|
+
* only to share a single access-list fetch between the dialog and an inline summary
|
|
21
|
+
* row: call it once, read `state.summary` for the row, and pass the controller into
|
|
22
|
+
* the dialog.
|
|
20
23
|
*
|
|
21
24
|
* @internal
|
|
22
25
|
*/
|
|
23
|
-
export declare function useObjectShareController(target: IObjectPermissionsObject | undefined,
|
|
26
|
+
export declare function useObjectShareController(target: IObjectPermissionsObject | undefined, options?: IUseObjectShareOptions): IObjectShareController;
|
|
24
27
|
//# sourceMappingURL=useObjectShareController.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useObjectShareController.d.ts","sourceRoot":"","sources":["../../src/share/useObjectShareController.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"useObjectShareController.d.ts","sourceRoot":"","sources":["../../src/share/useObjectShareController.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAa1E,OAAO,EACH,KAAK,sBAAsB,EAI3B,KAAK,sBAAsB,EAE9B,MAAM,kCAAkC,CAAC;AAI1C;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,wBAAwB,CACpC,MAAM,EAAE,wBAAwB,GAAG,SAAS,EAC5C,OAAO,CAAC,EAAE,sBAAsB,GACjC,sBAAsB,CAggBxB"}
|