@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.
- package/NOTICE +4 -4
- package/esm/index.d.ts +3 -2
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +3 -1
- package/esm/internal/translations/en-US.localization-bundle.d.ts +4 -0
- package/esm/internal/translations/en-US.localization-bundle.d.ts.map +1 -1
- package/esm/internal/translations/en-US.localization-bundle.js +4 -0
- package/esm/sdk-ui-ext.d.ts +56 -305
- package/esm/share/ObjectShareDialog.d.ts +19 -23
- package/esm/share/ObjectShareDialog.d.ts.map +1 -1
- package/esm/share/ObjectShareDialog.js +85 -171
- package/esm/share/accessSummary.d.ts +18 -8
- package/esm/share/accessSummary.d.ts.map +1 -1
- package/esm/share/accessSummary.js +25 -13
- package/esm/share/messages.d.ts +7 -0
- package/esm/share/messages.d.ts.map +1 -1
- package/esm/share/messages.js +5 -0
- package/esm/share/objectShareController.helpers.d.ts +83 -5
- package/esm/share/objectShareController.helpers.d.ts.map +1 -1
- package/esm/share/objectShareController.helpers.js +144 -10
- package/esm/share/objectShareController.types.d.ts +63 -64
- package/esm/share/objectShareController.types.d.ts.map +1 -1
- package/esm/share/types.d.ts +4 -5
- package/esm/share/types.d.ts.map +1 -1
- package/esm/share/useAccessList.d.ts +64 -63
- package/esm/share/useAccessList.d.ts.map +1 -1
- package/esm/share/useAccessList.js +174 -221
- package/esm/share/useLabelScope.d.ts +18 -13
- package/esm/share/useLabelScope.d.ts.map +1 -1
- package/esm/share/useLabelScope.js +112 -123
- package/esm/share/useObjectShareController.d.ts +14 -17
- package/esm/share/useObjectShareController.d.ts.map +1 -1
- package/esm/share/useObjectShareController.js +211 -224
- package/esm/share/useObjectShareDialog.d.ts +90 -0
- package/esm/share/useObjectShareDialog.d.ts.map +1 -0
- package/esm/share/useObjectShareDialog.js +82 -0
- package/package.json +21 -21
- package/src/notificationsPanel/NotificationsPanel/DefaultNotificationsPanelButton.scss +4 -1
- package/styles/css/main.css +1 -1
- 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
|
|
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
|
|
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*
|
|
65
|
-
* group)
|
|
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,
|
|
69
|
-
|
|
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,
|
|
78
|
-
|
|
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
|
-
/**
|
|
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 /
|
|
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
|
-
*
|
|
61
|
-
* current direct `level`. Retained from the fetch so `effectivePermission`
|
|
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
|
-
|
|
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?:
|
|
76
|
+
error?: GoodDataSdkError;
|
|
77
|
+
summary: IObjectAccessSummary | undefined;
|
|
78
|
+
grantees: IObjectShareGrantee[];
|
|
79
79
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* the
|
|
83
|
-
*
|
|
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
|
-
|
|
87
|
-
summary: IObjectAccessSummary | undefined;
|
|
85
|
+
selfManagedGranteeId: string | undefined;
|
|
88
86
|
/**
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
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
|
-
|
|
92
|
+
selfManagedDisabledLevels: ObjectSharePermissionLevel[] | undefined;
|
|
94
93
|
/**
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
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
|
-
|
|
98
|
+
workspaceDisabledLevels: ObjectSharePermissionLevel[] | undefined;
|
|
100
99
|
/**
|
|
101
|
-
* Whether
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
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
|
-
|
|
105
|
+
granteeControlsLocked: boolean;
|
|
107
106
|
/**
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
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
|
-
|
|
114
|
-
|
|
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.
|
|
120
|
-
* never offered for the workspace rule.
|
|
123
|
+
* not shown) while general access is RESTRICTED.
|
|
121
124
|
*/
|
|
122
|
-
workspaceLevel:
|
|
125
|
+
workspaceLevel: ObjectSharePermissionLevel;
|
|
123
126
|
/**
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
* too, yet cannot be revoked from here — consumers disable
|
|
127
|
-
* option and
|
|
128
|
-
*
|
|
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
|
-
|
|
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
|
|
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
|
|
199
|
-
*
|
|
200
|
-
*
|
|
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:
|
|
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
|
|
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.
|
|
239
|
-
*
|
|
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":"AAEA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC5E,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAEzG,OAAO,KAAK,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE1E
|
|
1
|
+
{"version":3,"file":"objectShareController.types.d.ts","sourceRoot":"","sources":["../../src/share/objectShareController.types.ts"],"names":[],"mappings":"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"}
|
package/esm/share/types.d.ts
CHANGED
|
@@ -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
|
|
35
|
-
*
|
|
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:
|
|
43
|
+
workspaceLevel: AccessGranularPermission;
|
|
45
44
|
granteeCount: number;
|
|
46
45
|
}
|
|
47
46
|
//# sourceMappingURL=types.d.ts.map
|
package/esm/share/types.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
|
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
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
16
|
+
/** Display rows: the fetched list composed with the local edit overlay. */
|
|
17
17
|
grantees: IObjectShareGrantee[];
|
|
18
18
|
/**
|
|
19
|
-
* Whether the
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
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
|
-
|
|
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
|
|
30
|
-
*
|
|
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
|
|
41
|
-
workspaceLevel:
|
|
35
|
+
/** Workspace-rule permission level — the rule overlay if edited, else fetched. */
|
|
36
|
+
workspaceLevel: ObjectSharePermissionLevel;
|
|
42
37
|
/**
|
|
43
|
-
* Strongest workspace
|
|
44
|
-
*
|
|
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
|
-
|
|
50
|
-
/**
|
|
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
|
-
/**
|
|
46
|
+
/** Load status surfaced as the controller status. */
|
|
53
47
|
status: IObjectShareControllerState["status"];
|
|
54
|
-
/**
|
|
55
|
-
loadError:
|
|
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
|
-
*
|
|
69
|
-
*
|
|
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
|
-
|
|
72
|
-
/**
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
*
|
|
80
|
-
*
|
|
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
|
-
|
|
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
|
|
87
|
-
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
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
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
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
|
|
101
|
+
export declare function useAccessList(target: IObjectPermissionsObject | undefined): IAccessList;
|
|
101
102
|
//# sourceMappingURL=useAccessList.d.ts.map
|