@gooddata/sdk-ui-ext 11.51.0-alpha.0 → 11.51.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/NOTICE +4 -4
  2. package/esm/index.d.ts +3 -2
  3. package/esm/index.d.ts.map +1 -1
  4. package/esm/index.js +3 -1
  5. package/esm/internal/translations/en-US.localization-bundle.d.ts +4 -0
  6. package/esm/internal/translations/en-US.localization-bundle.d.ts.map +1 -1
  7. package/esm/internal/translations/en-US.localization-bundle.js +4 -0
  8. package/esm/sdk-ui-ext.d.ts +56 -305
  9. package/esm/share/ObjectShareDialog.d.ts +19 -23
  10. package/esm/share/ObjectShareDialog.d.ts.map +1 -1
  11. package/esm/share/ObjectShareDialog.js +85 -171
  12. package/esm/share/accessSummary.d.ts +18 -8
  13. package/esm/share/accessSummary.d.ts.map +1 -1
  14. package/esm/share/accessSummary.js +25 -13
  15. package/esm/share/messages.d.ts +7 -0
  16. package/esm/share/messages.d.ts.map +1 -1
  17. package/esm/share/messages.js +5 -0
  18. package/esm/share/objectShareController.helpers.d.ts +83 -5
  19. package/esm/share/objectShareController.helpers.d.ts.map +1 -1
  20. package/esm/share/objectShareController.helpers.js +144 -10
  21. package/esm/share/objectShareController.types.d.ts +63 -64
  22. package/esm/share/objectShareController.types.d.ts.map +1 -1
  23. package/esm/share/types.d.ts +4 -5
  24. package/esm/share/types.d.ts.map +1 -1
  25. package/esm/share/useAccessList.d.ts +64 -63
  26. package/esm/share/useAccessList.d.ts.map +1 -1
  27. package/esm/share/useAccessList.js +174 -221
  28. package/esm/share/useLabelScope.d.ts +18 -13
  29. package/esm/share/useLabelScope.d.ts.map +1 -1
  30. package/esm/share/useLabelScope.js +112 -123
  31. package/esm/share/useObjectShareController.d.ts +14 -17
  32. package/esm/share/useObjectShareController.d.ts.map +1 -1
  33. package/esm/share/useObjectShareController.js +211 -224
  34. package/esm/share/useObjectShareDialog.d.ts +90 -0
  35. package/esm/share/useObjectShareDialog.d.ts.map +1 -0
  36. package/esm/share/useObjectShareDialog.js +82 -0
  37. package/package.json +21 -21
  38. package/src/notificationsPanel/NotificationsPanel/DefaultNotificationsPanelButton.scss +4 -1
  39. package/styles/css/main.css +1 -1
  40. package/styles/css/main.css.map +1 -1
@@ -27,7 +27,7 @@ export function assigneeIdentityFacts(assignee) {
27
27
  ? userIdentityFacts(assignee.ref, assignee.name, assignee.email)
28
28
  : { name: groupNameFact(assignee.ref, assignee.name) };
29
29
  }
30
- /** Display pair per the spec's fallback order: name + email → name + userID → email + userID → userID. */
30
+ /** Display pair, falling back: name + email → name + userID → email + userID → userID. */
31
31
  export function userDisplayPair(facts, userId) {
32
32
  if (facts.name) {
33
33
  return { name: facts.name, email: facts.email ?? userId };
@@ -45,6 +45,45 @@ export function granteeDisplayPair(grantee) {
45
45
  }
46
46
  return userDisplayPair(grantee, id);
47
47
  }
48
+ /**
49
+ * Deterministic display order for grantee rows — the backend returns them
50
+ * unsorted (and in an order that shuffles across reloads). Own row first, then
51
+ * groups, then users; within each, case-insensitive by display name with the
52
+ * grantee id as a stable tiebreaker so equal names don't reshuffle. Returns a
53
+ * new array; the input is not mutated.
54
+ */
55
+ export function sortGrantees(grantees) {
56
+ const rank = (g) => (g.isSelf ? 0 : g.kind === "group" ? 1 : 2);
57
+ return grantees.slice().sort((a, b) => {
58
+ const byRank = rank(a) - rank(b);
59
+ if (byRank !== 0) {
60
+ return byRank;
61
+ }
62
+ const byName = granteeDisplayPair(a).name.localeCompare(granteeDisplayPair(b).name, undefined, {
63
+ sensitivity: "base",
64
+ });
65
+ return byName === 0 ? a.id.localeCompare(b.id) : byName;
66
+ });
67
+ }
68
+ /**
69
+ * Deterministic display order for an attribute's labels — the backend returns
70
+ * display forms in an order that isn't guaranteed stable. Primary (key) label
71
+ * first, then the rest case-insensitive by title with the label id as a stable
72
+ * tiebreaker. Shared by the detail-page labels popup and the share dialog's
73
+ * label-access checklist so the two never disagree. Returns a new array; the
74
+ * input is not mutated.
75
+ *
76
+ * @internal
77
+ */
78
+ export function sortShareableLabels(labels) {
79
+ return labels.slice().sort((a, b) => {
80
+ if (a.isPrimary !== b.isPrimary) {
81
+ return a.isPrimary ? -1 : 1;
82
+ }
83
+ const byTitle = a.title.localeCompare(b.title, undefined, { sensitivity: "base" });
84
+ return byTitle === 0 ? a.id.localeCompare(b.id) : byTitle;
85
+ });
86
+ }
48
87
  /** Case-insensitive match of an assignee against the picker query (name, or email for users). */
49
88
  export function assigneeMatchesQuery(assignee, query) {
50
89
  if (!query) {
@@ -55,27 +94,44 @@ export function assigneeMatchesQuery(assignee, query) {
55
94
  }
56
95
  /** Permission levels from strongest to weakest; the row shows the strongest it holds. */
57
96
  const LEVELS_STRONGEST_FIRST = ["EDIT", "SHARE", "VIEW"];
97
+ const LEVEL_RANK = { VIEW: 0, SHARE: 1, EDIT: 2 };
98
+ /** The strongest permission level present, or undefined when none. */
99
+ export function strongestLevel(permissions) {
100
+ return LEVELS_STRONGEST_FIRST.find((level) => permissions.includes(level));
101
+ }
58
102
  /** The row's directly-granted level — the strongest permission present, defaulting to VIEW. */
59
103
  export function directLevel(permissions) {
60
- return LEVELS_STRONGEST_FIRST.find((level) => permissions.includes(level)) ?? "VIEW";
104
+ return strongestLevel(permissions) ?? "VIEW";
105
+ }
106
+ /**
107
+ * Levels strictly above the given one, e.g. for disabling levels the caller can't
108
+ * grant. An undefined bound means "unknown" — nothing is above it, so no level is
109
+ * disabled.
110
+ */
111
+ export function levelsAbove(level) {
112
+ return level === undefined ? [] : LEVELS_STRONGEST_FIRST.filter((l) => LEVEL_RANK[l] > LEVEL_RANK[level]);
113
+ }
114
+ /** Levels strictly below the given one, e.g. for disabling levels an inherited grant already exceeds. */
115
+ export function levelsBelow(level) {
116
+ return LEVELS_STRONGEST_FIRST.filter((l) => LEVEL_RANK[l] < LEVEL_RANK[level]);
61
117
  }
62
118
  /**
63
119
  * The effective permission to surface as a warning, or undefined when the direct
64
- * grant already covers it. Set only when the grantee *inherits* SHARE (e.g. via a
65
- * group) but is directly granted just VIEW — i.e. the effective access is higher
120
+ * grant already covers it. Set only when the grantee *inherits* a level (e.g. via
121
+ * a group) above the directly-granted one — i.e. the effective access is higher
66
122
  * than what the row's permission control shows.
67
123
  */
68
- export function effectivePermissionAbove(direct, inheritedPermissions) {
69
- // A direct EDIT already outranks any inherited SHARE — never warn in that case.
70
- return direct === "VIEW" && inheritedPermissions.includes("SHARE") ? "SHARE" : undefined;
124
+ export function effectivePermissionAbove(direct, inheritedLevel) {
125
+ return inheritedLevel && LEVEL_RANK[inheritedLevel] > LEVEL_RANK[direct] ? inheritedLevel : undefined;
71
126
  }
72
127
  /** The permission-derived fields shared by every grantee row, regardless of kind. */
73
128
  function granteeAccess(permissions, inheritedPermissions) {
74
129
  const level = directLevel(permissions);
130
+ const inheritedLevel = strongestLevel(inheritedPermissions);
75
131
  return {
76
132
  level,
77
- effectivePermission: effectivePermissionAbove(level, inheritedPermissions),
78
- inheritsShare: inheritedPermissions.includes("SHARE"),
133
+ effectivePermission: effectivePermissionAbove(level, inheritedLevel),
134
+ inheritedLevel,
79
135
  };
80
136
  }
81
137
  export function granteesFromAccessList(list) {
@@ -105,7 +161,85 @@ export function granteesFromAccessList(list) {
105
161
  }
106
162
  return out;
107
163
  }
108
- /** The permission set a grant carries at each level; a higher level always implies VIEW. */
164
+ /**
165
+ * Pure composition of the fetched base with the transient overlay — the hook's
166
+ * `grantees` derive from this with no effect and no mirrored state. Marks the
167
+ * caller's own row via `selfId`, backfilling its facts from `selfIdentity` (the
168
+ * self row is absent from the grant list by design). Input arrays are not mutated.
169
+ *
170
+ * @internal
171
+ */
172
+ export function mergeGrantees(base, edits, selfId, selfIdentity) {
173
+ const markSelf = (g) => {
174
+ const isSelf = g.id === selfId;
175
+ return isSelf && selfIdentity
176
+ ? { ...g, isSelf, name: g.name ?? selfIdentity.name, email: g.email ?? selfIdentity.email }
177
+ : { ...g, isSelf };
178
+ };
179
+ // The row an id last COMMITTED to: an added `settled` is that row outright (the
180
+ // base may hold a stale pre-removal version of the same id), a level `settled`
181
+ // overlays the base row, otherwise the base row itself (undefined for an
182
+ // overlay-born id with no history).
183
+ const committedRow = (g, settled) => {
184
+ if (settled?.kind === "added") {
185
+ return settled.grantee;
186
+ }
187
+ if (g && settled?.kind === "level") {
188
+ return {
189
+ ...g,
190
+ level: settled.level,
191
+ effectivePermission: effectivePermissionAbove(settled.level, g.inheritedLevel),
192
+ };
193
+ }
194
+ return g;
195
+ };
196
+ // One renderer per id: for each id, exactly one of the branches below emits at
197
+ // most one row — every base id resolves here, and the loop after handles only
198
+ // ids born in the overlay. Growing this case-by-case instead bred duplicate-row
199
+ // bugs whenever an edit history (re-add, re-remove) crossed the base boundary.
200
+ const renderEntry = (g, edit) => {
201
+ if (!edit) {
202
+ return g;
203
+ }
204
+ if (edit.kind === "removed") {
205
+ // Visible (muted, at its last committed state) only while the revoke is
206
+ // in flight; a settled removal renders nothing — its entry persists to
207
+ // keep the base row hidden.
208
+ const committed = edit.pending ? committedRow(g, edit.settled) : undefined;
209
+ return committed ? { ...committed, pending: "removing" } : undefined;
210
+ }
211
+ if (edit.kind === "level") {
212
+ return g
213
+ ? {
214
+ ...g,
215
+ level: edit.level,
216
+ effectivePermission: effectivePermissionAbove(edit.level, g.inheritedLevel),
217
+ pending: edit.pending ? "saving" : undefined,
218
+ }
219
+ : undefined;
220
+ }
221
+ return { ...edit.grantee, pending: edit.pending ? "saving" : undefined };
222
+ };
223
+ const out = [];
224
+ const baseIds = new Set(base.map((g) => g.id));
225
+ for (const g of base) {
226
+ const row = renderEntry(g, edits[g.id]);
227
+ if (row) {
228
+ out.push(markSelf(row));
229
+ }
230
+ }
231
+ for (const [id, edit] of Object.entries(edits)) {
232
+ if (baseIds.has(id)) {
233
+ continue;
234
+ }
235
+ const row = renderEntry(undefined, edit);
236
+ if (row) {
237
+ out.push(markSelf(row));
238
+ }
239
+ }
240
+ return out;
241
+ }
242
+ /** Permission set per level; a higher level always implies VIEW. */
109
243
  const PERMISSIONS_BY_LEVEL = {
110
244
  none: [],
111
245
  VIEW: ["VIEW"],
@@ -1,12 +1,10 @@
1
1
  import type { AccessGranularPermission, ObjRef } from "@gooddata/sdk-model";
2
+ import type { GoodDataSdkError } from "@gooddata/sdk-ui";
2
3
  import type { GeneralAccessValue, IUiGranteeAsyncOptions, IUiPickedGrantee } from "@gooddata/sdk-ui-kit";
3
4
  import type { IObjectAccessSummary, IObjectShareLabel } from "./types.js";
4
5
  /**
5
6
  * Permission level surfaced in the share dialog — the model's granular access
6
- * permission (VIEW / EDIT / SHARE). `EDIT` is display-only: the dialog reflects
7
- * an EDIT grant as a read-only "Can edit" row but cannot assign or change it
8
- * (granting EDIT is not part of the share UI), so only VIEW and SHARE are
9
- * selectable in the permission menu.
7
+ * permission (VIEW / SHARE / EDIT), assignable from the permission menu.
10
8
  *
11
9
  * @internal
12
10
  */
@@ -57,11 +55,11 @@ export interface IObjectShareGrantee extends IGranteeIdentityFacts {
57
55
  */
58
56
  effectivePermission?: ObjectSharePermissionLevel;
59
57
  /**
60
- * Whether the grantee inherits SHARE (e.g. via a group), regardless of the
61
- * current direct `level`. Retained from the fetch so `effectivePermission` can
62
- * be recomputed locally when the direct level changes (no refetch).
58
+ * Strongest permission the grantee inherits (e.g. via a group), regardless of
59
+ * the current direct `level`. Retained from the fetch so `effectivePermission`
60
+ * can be recomputed locally when the direct level changes (no refetch).
63
61
  */
64
- inheritsShare?: boolean;
62
+ inheritedLevel?: ObjectSharePermissionLevel;
65
63
  /**
66
64
  * Row-level in-flight state for optimistic updates: `"saving"` while a level
67
65
  * change or freshly-added grant is being committed, `"removing"` while a
@@ -75,63 +73,68 @@ export interface IObjectShareGrantee extends IGranteeIdentityFacts {
75
73
  export interface IObjectShareControllerState {
76
74
  subview: "main" | "addGrantee";
77
75
  status: "idle" | "loading" | "success" | "error" | "saving";
78
- error?: Error;
76
+ error?: GoodDataSdkError;
77
+ summary: IObjectAccessSummary | undefined;
78
+ grantees: IObjectShareGrantee[];
79
79
  /**
80
- * Whether object-level permissions are unavailable to the current user — the
81
- * manage-gated access-list endpoint returned 404, the backend's signal that
82
- * the caller cannot manage this object's sharing. Distinct from a transient
83
- * load error (5xx / network), which may still resolve: consumers use this to
84
- * hide the share UI entirely rather than to retry.
80
+ * Id of the grantee row whose permission menu manages the signed-in user's
81
+ * OWN access set only when the sole explicit grant is the caller's own
82
+ * (the "restrict own access" design). Consumers gate lowering behind a
83
+ * confirm and disable the levels above the row's own.
85
84
  */
86
- accessUnavailable: boolean;
87
- summary: IObjectAccessSummary | undefined;
85
+ selfManagedGranteeId: string | undefined;
88
86
  /**
89
- * Stable serialized key of the current target's ref, or undefined when none.
90
- * Consumers key target-scoped transient UI (e.g. a staged confirmation) on it,
91
- * so nothing staged for one object can be applied to another.
87
+ * Levels the self-managed row's permission menu must disable — those above the
88
+ * caller's own current level (you can't raise yourself). Set exactly when
89
+ * `selfManagedGranteeId` is; every other row is unconstrained here (the backend
90
+ * is the authority on what may be granted).
92
91
  */
93
- targetKey: string | undefined;
92
+ selfManagedDisabledLevels: ObjectSharePermissionLevel[] | undefined;
94
93
  /**
95
- * Signed-in user's identity, once the profile resolves. Feeds their
96
- * synthesized row when they can manage the object without holding any
97
- * grant (administrator access) and the grantee list is empty.
94
+ * Levels the workspace-rule permission menu must disable those below an
95
+ * inherited workspace grant, which can't be lowered from this workspace.
96
+ * Undefined when no level is inherited.
98
97
  */
99
- selfIdentity: ISelfIdentity | undefined;
98
+ workspaceDisabledLevels: ObjectSharePermissionLevel[] | undefined;
100
99
  /**
101
- * Whether the profile request resolved successfully. Until then or after a
102
- * silently-swallowed failure `isSelf` on the rows is only its unresolved
103
- * default, so a sole grantee row cannot be told apart from the caller's own
104
- * grant and must not be offered destructive controls.
100
+ * Whether grantee-row controls must stay disabled because a sole USER row
101
+ * cannot be told apart from the caller's own grant yet (profile pending or
102
+ * silently failed) mutating it then would bypass the self-restriction
103
+ * confirm. Group rows and multi-row lists never set this.
105
104
  */
106
- selfIdentityResolved: boolean;
105
+ granteeControlsLocked: boolean;
107
106
  /**
108
- * Whether the manage-gated access list was LOADED with zero explicit grants —
109
- * the caller reached it without holding a grant of their own. A load-time
110
- * fact (never recomputed from the live list): removing your own sole grant
111
- * empties the list but does not make this true.
107
+ * Display pair for the synthesized administrator self row, or undefined when
108
+ * no such row applies. Set while the DISPLAYED list is empty for a caller whose
109
+ * list loaded without a grant of their own and no workspace-wide share-capable
110
+ * rule explains the access they can only have passed the backend's gate
111
+ * through administrator/manager rights. Adding a grantee hides the row;
112
+ * removing the last one brings it back. (Derived from the immutable seed:
113
+ * removing your own grant empties the list but never synthesizes this row.)
112
114
  */
113
- seededWithoutGrants: boolean;
114
- grantees: IObjectShareGrantee[];
115
+ adminSelfRow: {
116
+ name: string;
117
+ email?: string;
118
+ } | undefined;
115
119
  generalAccess: GeneralAccessValue;
116
120
  /**
117
121
  * Permission level of the all-workspace-members rule when general access is
118
122
  * WORKSPACE. Drives the workspace row's permission dropdown. Meaningless (and
119
- * not shown) while general access is RESTRICTED. Capped at VIEW/SHARE — EDIT is
120
- * never offered for the workspace rule.
123
+ * not shown) while general access is RESTRICTED.
121
124
  */
122
- workspaceLevel: "VIEW" | "SHARE";
125
+ workspaceLevel: ObjectSharePermissionLevel;
123
126
  /**
124
- * Whether workspace-wide access is (at least partly) inherited from a parent
125
- * workspace. Inherited rule access grants every user of this workspace access
126
- * too, yet cannot be revoked from here — consumers disable the Restricted
127
- * option and explain why. `generalAccess` and `workspaceLevel` are effective
128
- * values that already account for it.
127
+ * Strongest workspace-wide level inherited from parent workspaces, or
128
+ * undefined when none. Inherited access grants every user of this workspace
129
+ * access too, yet cannot be revoked or lowered from here — consumers disable
130
+ * the Restricted option and the workspace-rule levels below this.
131
+ * `generalAccess` and `workspaceLevel` already account for it.
129
132
  */
130
- workspaceAccessInherited: boolean;
133
+ workspaceInheritedLevel: ObjectSharePermissionLevel | undefined;
131
134
  /**
132
135
  * Whether the workspace-rule level dropdown must be read-only: this workspace
133
136
  * holds no rule of its own to re-grade (access is inherited-only), or an
134
- * inherited SHARE pins the effective level regardless of the direct grant.
137
+ * inherited EDIT pins the effective level regardless of the direct grant.
135
138
  */
136
139
  workspaceLevelLocked: boolean;
137
140
  /**
@@ -152,6 +155,14 @@ export interface IObjectShareControllerState {
152
155
  * keep the Add action disabled.
153
156
  */
154
157
  labelsResolved: boolean;
158
+ /**
159
+ * True until the session's FIRST label-scope resolution settles. Unlike
160
+ * {@link labelsResolved} it never turns back on (a mid-session re-probe only
161
+ * re-disables controls), and a resolution that settles with failures counts
162
+ * too — so loading placeholders keyed on it can't stick forever or flash
163
+ * back mid-session.
164
+ */
165
+ labelsInitializing: boolean;
155
166
  /**
156
167
  * Per-grantee label scope: grantee id → the label ids that grantee can access.
157
168
  * The primary label is always included. Empty entry means "all labels" has not
@@ -167,8 +178,6 @@ export interface IObjectShareControllerState {
167
178
  * @internal
168
179
  */
169
180
  export interface IObjectShareControllerActions {
170
- /** Reset transient dialog state (subview + pending buffers). Call on dialog close. */
171
- reset: () => void;
172
181
  openAddGrantee: () => void;
173
182
  closeAddGrantee: () => void;
174
183
  setPendingGrantees: (next: IUiPickedGrantee[]) => void;
@@ -195,12 +204,12 @@ export interface IObjectShareControllerActions {
195
204
  /** Commit the pending general access change. Auto-saves. */
196
205
  confirmGeneralAccessChange: () => Promise<void>;
197
206
  /**
198
- * Change the all-workspace-members rule's permission level (VIEW/SHARE).
199
- * Only meaningful while general access is WORKSPACE. Auto-saves, no confirm —
200
- * unlike the high-impact RESTRICTED↔WORKSPACE toggle, this only re-grades an
207
+ * Change the all-workspace-members rule's permission level. Only meaningful
208
+ * while general access is WORKSPACE. Auto-saves, no confirm — unlike the
209
+ * high-impact RESTRICTED↔WORKSPACE toggle, this only re-grades an
201
210
  * already-granted rule.
202
211
  */
203
- changeWorkspaceLevel: (level: "VIEW" | "SHARE") => Promise<void>;
212
+ changeWorkspaceLevel: (level: ObjectSharePermissionLevel) => Promise<void>;
204
213
  }
205
214
  /**
206
215
  * @internal
@@ -210,17 +219,11 @@ export interface IObjectShareController {
210
219
  actions: IObjectShareControllerActions;
211
220
  }
212
221
  /**
213
- * Options for {@link useObjectShare}.
222
+ * Options for {@link useObjectShareController}.
214
223
  *
215
224
  * @internal
216
225
  */
217
226
  export interface IUseObjectShareOptions {
218
- /**
219
- * Fires after each successful access mutation (add grantee, change level,
220
- * remove, general access toggle). Use it to keep UI outside the dialog in
221
- * sync with edits made inside it (e.g. refresh an inline access row).
222
- */
223
- onSaved?: () => void;
224
227
  /**
225
228
  * Labels (display forms) of the shared attribute, enabling the per-grantee
226
229
  * label-scope picker. Omit for objects without labels (e.g. facts).
@@ -235,13 +238,9 @@ export interface IUseObjectShareOptions {
235
238
  */
236
239
  labelsError?: boolean;
237
240
  /**
238
- * Whether the object's labels are still loading. While true the controller stays
239
- * label-unresolved (same gating as `labelsError`): the labels aren't passed yet,
240
- * so an empty list must not be mistaken for a label-free object — otherwise row
241
- * controls would reconcile against an empty set and orphan real per-label grants.
241
+ * Whether the object's labels are still loading. Same gating as `labelsError`:
242
+ * the labels aren't passed yet, so an empty list must not read as label-free.
242
243
  */
243
244
  labelsLoading?: boolean;
244
- /** Whether the share dialog is open; a summary-only consumer leaves it false. */
245
- isOpen?: boolean;
246
245
  }
247
246
  //# sourceMappingURL=objectShareController.types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"objectShareController.types.d.ts","sourceRoot":"","sources":["../../src/share/objectShareController.types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"objectShareController.types.d.ts","sourceRoot":"","sources":["../../src/share/objectShareController.types.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { ObjRef } from "@gooddata/sdk-model";
1
+ import type { AccessGranularPermission, ObjRef } from "@gooddata/sdk-model";
2
2
  import type { GeneralAccessValue } from "@gooddata/sdk-ui-kit";
3
3
  /**
4
4
  * A label (attribute display form) of the shared attribute. The share dialog
@@ -31,9 +31,8 @@ export interface IObjectShareLabel {
31
31
  *
32
32
  * - `generalAccess` — `RESTRICTED` if only named grantees can access the object;
33
33
  * `WORKSPACE` if the workspace-wide rule grant is present with non-empty permissions.
34
- * - `workspaceLevel` — the rule grant's permission level; `VIEW` by default,
35
- * `SHARE` when the rule explicitly permits `SHARE`. EDIT is intentionally not
36
- * surfaced (the UI caps at VIEW/SHARE).
34
+ * - `workspaceLevel` — the rule grant's permission level: the strongest level the
35
+ * rule permits, `VIEW` by default.
37
36
  * - `granteeCount` — number of named grantees (users + groups). Excludes the
38
37
  * workspace-wide rule grant itself.
39
38
  *
@@ -41,7 +40,7 @@ export interface IObjectShareLabel {
41
40
  */
42
41
  export interface IObjectAccessSummary {
43
42
  generalAccess: GeneralAccessValue;
44
- workspaceLevel: "VIEW" | "SHARE";
43
+ workspaceLevel: AccessGranularPermission;
45
44
  granteeCount: number;
46
45
  }
47
46
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/share/types.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAC9B,+EAA+E;IAC/E,GAAG,EAAE,MAAM,CAAC;IACZ,6EAA2E;IAC3E,EAAE,EAAE,MAAM,CAAC;IACX,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,SAAS,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,oBAAoB;IACjC,aAAa,EAAE,kBAAkB,CAAC;IAClC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;CACxB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/share/types.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC5E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAC9B,+EAA+E;IAC/E,GAAG,EAAE,MAAM,CAAC;IACZ,6EAA2E;IAC3E,EAAE,EAAE,MAAM,CAAC;IACX,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,SAAS,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,SAAS,EAAE,OAAO,CAAC;CACtB;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,oBAAoB;IACjC,aAAa,EAAE,kBAAkB,CAAC;IAClC,cAAc,EAAE,wBAAwB,CAAC;IACzC,YAAY,EAAE,MAAM,CAAC;CACxB"}
@@ -1,7 +1,9 @@
1
1
  import type { IObjectPermissionsObject } from "@gooddata/sdk-backend-spi";
2
- import { type IGranularAccessGrantee, type ObjRef } from "@gooddata/sdk-model";
2
+ import { type IGranularAccessGrantee } from "@gooddata/sdk-model";
3
+ import { type GoodDataSdkError } from "@gooddata/sdk-ui";
3
4
  import { type GeneralAccessValue, type IUiGranteeAsyncOptions } from "@gooddata/sdk-ui-kit";
4
- import type { IObjectShareControllerState, IObjectShareGrantee, ISelfIdentity } from "./objectShareController.types.js";
5
+ import { type IRuleEdit } from "./objectShareController.helpers.js";
6
+ import type { IObjectShareControllerState, IObjectShareGrantee, ISelfIdentity, ObjectSharePermissionLevel } from "./objectShareController.types.js";
5
7
  import type { IObjectAccessSummary } from "./types.js";
6
8
  /**
7
9
  * The owned access list for {@link useObjectShareController}.
@@ -9,93 +11,92 @@ import type { IObjectAccessSummary } from "./types.js";
9
11
  * @internal
10
12
  */
11
13
  export interface IAccessList {
12
- /** Stable serialized key of the current target's ref, or undefined when none. */
13
- targetKey: string | undefined;
14
- /** True once the current target's list has been fetched and seeded into local state. */
14
+ /** True once the target's list has been fetched. */
15
15
  hasList: boolean;
16
- /** Local grantee rows seeded from the fetch, then authoritative; mutations write through. */
16
+ /** Display rows: the fetched list composed with the local edit overlay. */
17
17
  grantees: IObjectShareGrantee[];
18
18
  /**
19
- * Whether the manage-gated fetch seeded ZERO explicit grants for the current
20
- * target i.e. the caller reached the access list without holding a grant of
21
- * their own (grant-independent access). A load-time fact: locally emptying the
22
- * list later (e.g. removing your own sole grant) must not retroactively claim
23
- * grant-independent access, so this is never recomputed from the live list.
19
+ * Whether the fetched list held no grant for the SIGNED-IN user — they reached
20
+ * this manage-gated list without a grant of their own (grant-independent access:
21
+ * admin/manager rights, or a workspace rule reported separately). Derived from
22
+ * the immutable seed, never the live overlay: a caller whose own grant was the
23
+ * way in must not read as grant-independent after locally removing it.
24
24
  */
25
- seededWithoutGrants: boolean;
25
+ seededWithoutSelfGrant: boolean;
26
26
  /** Signed-in user's identity facts + login id, once the profile resolves. */
27
27
  selfIdentity: ISelfIdentity | undefined;
28
28
  /**
29
- * Whether the profile request resolved successfully until then (or after a
30
- * failure) rows' `isSelf` is only its unresolved default, so a sole grantee
31
- * row cannot be told apart from the caller's own grant.
29
+ * Whether the profile resolved. Until then a sole grantee row can't be told apart
30
+ * from the caller's own grant, so its `isSelf` is only an unresolved default.
32
31
  */
33
32
  selfIdentityResolved: boolean;
34
- /**
35
- * Workspace vs restricted general access granted by THIS workspace's own rule —
36
- * local state; mutations write through. Excludes inherited (parent-workspace)
37
- * rule access, which is exposed separately as `inheritedWorkspaceLevel`.
38
- */
33
+ /** Direct general access (excludes inherited rule access). */
39
34
  generalAccess: GeneralAccessValue;
40
- /** Workspace-rule permission level (VIEW/SHARE) local state; mutations write through. */
41
- workspaceLevel: "VIEW" | "SHARE";
35
+ /** Workspace-rule permission level — the rule overlay if edited, else fetched. */
36
+ workspaceLevel: ObjectSharePermissionLevel;
42
37
  /**
43
- * Strongest workspace-wide level inherited from parent workspaces, or undefined
44
- * when none. Seeded from the fetch and never mutated: inherited access cannot
45
- * be changed from this workspace, so no write-through applies. Effective
46
- * (displayed) general access is WORKSPACE whenever this is set, regardless of
47
- * the direct `generalAccess` state.
38
+ * Strongest workspace level inherited from parent workspaces. Cannot be changed
39
+ * from here, so effective general access is WORKSPACE whenever it's set.
48
40
  */
49
- inheritedWorkspaceLevel: "VIEW" | "SHARE" | undefined;
50
- /** Inline access summary, or undefined before the first load. */
41
+ workspaceInheritedLevel: ObjectSharePermissionLevel | undefined;
42
+ /** True while a workspace-rule re-grade is committing (the rule overlay is pending). */
43
+ workspaceLevelSaving: boolean;
44
+ /** Access summary of the displayed state, or undefined before the first load. */
51
45
  summary: IObjectAccessSummary | undefined;
52
- /** Top-level load status surfaced as the controller status. */
46
+ /** Load status surfaced as the controller status. */
53
47
  status: IObjectShareControllerState["status"];
54
- /** Error from the initial/target-change load. */
55
- loadError: Error | undefined;
56
- /**
57
- * Whether the current target's load was denied because the caller can't manage
58
- * its permissions (manage-gated endpoint returns 404). Derived from the live
59
- * fetch, not the persisted `loadError`, so it can't lag a target switch and
60
- * flag a new target with the previous one's 404.
61
- */
62
- accessUnavailable: boolean;
48
+ /** Typed SDK error from the load, or undefined. */
49
+ loadError: GoodDataSdkError | undefined;
63
50
  /** Write a grant change to the backend and toast. False on failure; no refetch. */
64
51
  commit: (mutate: IGranularAccessGrantee[], successMessage: {
65
52
  id: string;
66
53
  }) => Promise<boolean>;
54
+ /** Picker loader — available assignees filtered by query, excluding already-granted ones. */
55
+ loadOptions: (search: string) => Promise<IUiGranteeAsyncOptions>;
67
56
  /**
68
- * Picker loader available assignees filtered by query, excluding
69
- * already-granted grantees (add-grantee picker).
57
+ * Overlay a freshly picked grantee as a pending added row. Applied synchronously,
58
+ * before the write. Re-adding a removed base row supersedes the committed removal
59
+ * (carried as `settled`, restored if the add fails).
70
60
  */
71
- loadOptions: (search: string) => Promise<IUiGranteeAsyncOptions>;
72
- /** Resolve a grantee id back to the picker's original ObjRef (preserves Uri vs Id ref). */
73
- refForId: (id: string) => ObjRef;
74
- /** Write through a local grantee-row change (insert / level / remove); rolled back by the caller. */
75
- setGrantees: React.Dispatch<React.SetStateAction<IObjectShareGrantee[]>>;
76
- /** Write through a local general-access change; rolled back by the caller. */
77
- setGeneralAccess: React.Dispatch<React.SetStateAction<GeneralAccessValue>>;
61
+ applyGranteeAdd: (grantee: IObjectShareGrantee) => void;
62
+ /**
63
+ * Overlay a pending level change. A row born in the overlay (added since the
64
+ * fetch) stays an `added` entry with the new level base doesn't hold it, so a
65
+ * plain level entry would drop the row from the merged view.
66
+ */
67
+ applyGranteeLevel: (id: string, level: ObjectSharePermissionLevel) => void;
68
+ /** Overlay a pending removal; the row renders muted until the write settles. */
69
+ applyGranteeRemove: (id: string) => void;
70
+ /** Commit the pending edit for `id` after a successful write. */
71
+ settleGranteeEdit: (id: string) => void;
78
72
  /**
79
- * Write through the local workspace-rule permission level. A general-access write
80
- * always grants workspace VIEW, so the caller sets this to keep the summary from
81
- * showing a stale SHARE inherited from the initial fetch.
73
+ * Revert the pending edit for `id` after a failed write: restore the committed
74
+ * entry it superseded, or drop the entry (back to the fetched row) if none.
82
75
  */
83
- setWorkspaceLevel: React.Dispatch<React.SetStateAction<"VIEW" | "SHARE">>;
76
+ failGranteeEdit: (id: string) => void;
77
+ /** Overlay the all-workspace-users rule edit (pending while a re-grade commits). */
78
+ applyRuleEdit: (edit: Omit<IRuleEdit, "settled">) => void;
79
+ /** Commit the pending rule edit after a successful write. */
80
+ settleRuleEdit: () => void;
81
+ /** Revert the rule edit after a failed write: the superseded committed edit, or the fetch. */
82
+ failRuleEdit: () => void;
84
83
  }
85
84
  /**
86
- * Owns the backend access list. It is fetched once (per target) then seeded into
87
- * local state, which is authoritative while the dialog/summary is mounted:
88
- * mutations write through to it directly and roll back the one changed entry on
89
- * failure. There is no post-write refetch, so the grantee list never blanks and
90
- * never fights the backend's read-after-write lag. A target switch re-seeds from
91
- * the new fetch; the seed is gated on a per-target stamp so a late fetch for a
92
- * previous target can't clobber the current one.
85
+ * Owns the backend access list for ONE dialog session: the hook is mounted while
86
+ * the dialog is open, for a single target that must not change while mounted
87
+ * (remount for a new target see {@link ObjectShareDialog}). Unmounting discards
88
+ * every transient: the edit overlay, the profile, in-flight finalizers.
93
89
  *
94
- * Effective (inherited) permissions and display names come from that single
95
- * fetch: inherited access doesn't change as a result of editing a direct grant,
96
- * so the badge stays correct for the session without re-reading.
90
+ * The flow is one-directional: the list is fetched once, and the displayed state
91
+ * is *derived* from that fetch composed with a small local edit overlay
92
+ * ({@link mergeGrantees}). There is no mirrored state and no post-write refetch —
93
+ * a mutation writes its overlay entry (so the row updates immediately), commits to
94
+ * the backend, then settles the entry on success or reverts it on failure — to the
95
+ * committed entry it superseded (kept as `settled` on the pending edit), or to the
96
+ * fetched row when there was none. The grantee list therefore never blanks and
97
+ * never fights read-after-write lag.
97
98
  *
98
99
  * @internal
99
100
  */
100
- export declare function useAccessList(target: IObjectPermissionsObject | undefined, onSaved: (() => void) | undefined, dialogOpen: boolean): IAccessList;
101
+ export declare function useAccessList(target: IObjectPermissionsObject | undefined): IAccessList;
101
102
  //# sourceMappingURL=useAccessList.d.ts.map