@burdenoff/fe-libs 2026.601.11 → 2026.601.13
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/dist/shared/actors/index.js +1 -1
- package/dist/shared/config/shellRoutes.d.ts.map +1 -1
- package/dist/shared/config/shellRoutes.js +7 -0
- package/dist/shared/tours/toursContext/useTourMutations.d.ts.map +1 -1
- package/dist/shared/tours/toursContext/useTourMutations.js +1 -10
- package/dist/shared.js +77 -77
- package/dist/sharing/AcceptSharePage.d.ts +28 -0
- package/dist/sharing/AcceptSharePage.d.ts.map +1 -0
- package/dist/sharing/AcceptSharePage.js +270 -0
- package/dist/sharing/MemberPicker.d.ts +26 -0
- package/dist/sharing/MemberPicker.d.ts.map +1 -0
- package/dist/sharing/MemberPicker.js +110 -0
- package/dist/sharing/PermissionSelect.d.ts +19 -0
- package/dist/sharing/PermissionSelect.d.ts.map +1 -0
- package/dist/sharing/PermissionSelect.js +30 -0
- package/dist/sharing/ShareDialog.d.ts +26 -0
- package/dist/sharing/ShareDialog.d.ts.map +1 -0
- package/dist/sharing/ShareDialog.js +318 -0
- package/dist/sharing/SharedWithList.d.ts +18 -0
- package/dist/sharing/SharedWithList.d.ts.map +1 -0
- package/dist/sharing/SharedWithList.js +184 -0
- package/dist/sharing/index.d.ts +9 -0
- package/dist/sharing/index.d.ts.map +1 -0
- package/dist/sharing/shareUrl.d.ts +20 -0
- package/dist/sharing/shareUrl.d.ts.map +1 -0
- package/dist/sharing/shareUrl.js +39 -0
- package/dist/sharing/types.d.ts +94 -0
- package/dist/sharing/types.d.ts.map +1 -0
- package/dist/sharing/types.js +25 -0
- package/dist/sharing/useWorkspaceDirectory.d.ts +43 -0
- package/dist/sharing/useWorkspaceDirectory.d.ts.map +1 -0
- package/dist/sharing/useWorkspaceDirectory.js +117 -0
- package/dist/sharing.d.ts +2 -0
- package/dist/sharing.js +9 -0
- package/package.json +5 -1
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { cn as e } from "../lib/utils.js";
|
|
2
|
+
import { Button as t } from "../ui/button.js";
|
|
3
|
+
import { Badge as n } from "../ui/badge.js";
|
|
4
|
+
import { ActorIdentity as r } from "../shared/actors/ActorIdentity.js";
|
|
5
|
+
import { PermissionSelect as i } from "./PermissionSelect.js";
|
|
6
|
+
import { useActorProfiles as a } from "../shared/actors/useActorProfiles.js";
|
|
7
|
+
import { copyTextToClipboard as o, normalizeShareUrl as s } from "./shareUrl.js";
|
|
8
|
+
import { useState as c } from "react";
|
|
9
|
+
import { Check as l, Clock as u, Copy as d, Link2 as f, Trash2 as p } from "lucide-react";
|
|
10
|
+
import { jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
11
|
+
//#region src/sharing/SharedWithList.tsx
|
|
12
|
+
function g(e) {
|
|
13
|
+
switch (e) {
|
|
14
|
+
case "ACCEPTED": return {
|
|
15
|
+
variant: "success",
|
|
16
|
+
label: "Accepted"
|
|
17
|
+
};
|
|
18
|
+
case "PENDING": return {
|
|
19
|
+
variant: "warning",
|
|
20
|
+
label: "Pending"
|
|
21
|
+
};
|
|
22
|
+
case "EXPIRED": return {
|
|
23
|
+
variant: "secondary",
|
|
24
|
+
label: "Expired"
|
|
25
|
+
};
|
|
26
|
+
case "REVOKED": return {
|
|
27
|
+
variant: "secondary",
|
|
28
|
+
label: "Revoked"
|
|
29
|
+
};
|
|
30
|
+
default: return {
|
|
31
|
+
variant: "info",
|
|
32
|
+
label: e
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function _(e) {
|
|
37
|
+
let t = new Date(e);
|
|
38
|
+
if (Number.isNaN(t.getTime())) return "";
|
|
39
|
+
let n = t.getTime() - Date.now();
|
|
40
|
+
if (n <= 0) return "Expired";
|
|
41
|
+
let r = Math.floor(n / (1e3 * 60 * 60));
|
|
42
|
+
return r > 24 ? `${Math.floor(r / 24)}d left` : r > 0 ? `${r}h left` : `${Math.floor(n / (1e3 * 60))}m left`;
|
|
43
|
+
}
|
|
44
|
+
function v(e, t) {
|
|
45
|
+
if (e.granteeUserId) return t[e.granteeUserId] || {
|
|
46
|
+
actorId: e.granteeUserId,
|
|
47
|
+
actorType: "user",
|
|
48
|
+
displayName: e.granteeEmail ?? `User ${e.granteeUserId.slice(0, 8)}`,
|
|
49
|
+
secondaryLabel: e.granteeEmail ?? e.granteeUserId,
|
|
50
|
+
email: e.granteeEmail ?? void 0,
|
|
51
|
+
resolved: !!e.granteeEmail
|
|
52
|
+
};
|
|
53
|
+
let n = e.granteeEmail ?? "Pending invite";
|
|
54
|
+
return {
|
|
55
|
+
actorId: e.id,
|
|
56
|
+
actorType: "user",
|
|
57
|
+
displayName: n,
|
|
58
|
+
secondaryLabel: n,
|
|
59
|
+
email: e.granteeEmail ?? void 0,
|
|
60
|
+
resolved: !!e.granteeEmail
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
var y = ({ controller: y, workspaceId: b, authToken: x, apiGatewayUrl: S, className: C }) => {
|
|
64
|
+
let { grants: w, links: T, capabilities: E } = y, [D, O] = c(null), k = a(w.filter((e) => !!e.granteeUserId).map((e) => ({
|
|
65
|
+
actorId: e.granteeUserId,
|
|
66
|
+
actorType: "user"
|
|
67
|
+
})), {
|
|
68
|
+
workspaceId: b,
|
|
69
|
+
authToken: x,
|
|
70
|
+
apiGatewayUrl: S
|
|
71
|
+
}), A = async (e) => {
|
|
72
|
+
e.shareUrl && await o(s(e.shareUrl)).then(() => !0, () => !1) && (O(e.id), setTimeout(() => O((t) => t === e.id ? null : t), 2e3));
|
|
73
|
+
};
|
|
74
|
+
return w.length > 0 || T.length > 0 ? /* @__PURE__ */ h("div", {
|
|
75
|
+
className: e("space-y-3", C),
|
|
76
|
+
"data-testid": "shared-with-list",
|
|
77
|
+
children: [w.length > 0 ? /* @__PURE__ */ h("div", {
|
|
78
|
+
className: "space-y-2",
|
|
79
|
+
children: [/* @__PURE__ */ m("h4", {
|
|
80
|
+
className: "text-xs font-medium uppercase tracking-wider text-text-tertiary",
|
|
81
|
+
children: "People with access"
|
|
82
|
+
}), w.map((e) => {
|
|
83
|
+
let a = g(e.status);
|
|
84
|
+
return /* @__PURE__ */ h("div", {
|
|
85
|
+
"data-testid": "share-row",
|
|
86
|
+
className: "flex items-center justify-between gap-3 rounded-md border border-border-subtle bg-bg-surface px-3 py-2",
|
|
87
|
+
children: [/* @__PURE__ */ h("div", {
|
|
88
|
+
className: "flex min-w-0 flex-1 items-center gap-2",
|
|
89
|
+
children: [/* @__PURE__ */ m(r, {
|
|
90
|
+
profile: v(e, k),
|
|
91
|
+
variant: "inline",
|
|
92
|
+
hideIdDrilldown: !0
|
|
93
|
+
}), /* @__PURE__ */ m(n, {
|
|
94
|
+
variant: a.variant,
|
|
95
|
+
children: a.label
|
|
96
|
+
})]
|
|
97
|
+
}), /* @__PURE__ */ h("div", {
|
|
98
|
+
className: "flex items-center gap-2",
|
|
99
|
+
children: [y.updateRole ? /* @__PURE__ */ m(i, {
|
|
100
|
+
value: e.role,
|
|
101
|
+
roles: E.roles,
|
|
102
|
+
onChange: (t) => {
|
|
103
|
+
y.updateRole?.(e.id, t);
|
|
104
|
+
},
|
|
105
|
+
className: "h-8 w-36",
|
|
106
|
+
testId: `share-row-role-${e.id}`
|
|
107
|
+
}) : /* @__PURE__ */ m("span", {
|
|
108
|
+
className: "text-sm text-text-secondary",
|
|
109
|
+
children: e.role
|
|
110
|
+
}), y.revoke ? /* @__PURE__ */ m(t, {
|
|
111
|
+
type: "button",
|
|
112
|
+
variant: "ghost",
|
|
113
|
+
size: "icon-sm",
|
|
114
|
+
"data-testid": "share-row-revoke",
|
|
115
|
+
"aria-label": "Revoke access",
|
|
116
|
+
onClick: () => {
|
|
117
|
+
y.revoke?.(e.id);
|
|
118
|
+
},
|
|
119
|
+
className: "text-text-secondary hover:text-status-error-text",
|
|
120
|
+
children: /* @__PURE__ */ m(p, { className: "size-4" })
|
|
121
|
+
}) : null]
|
|
122
|
+
})]
|
|
123
|
+
}, e.id);
|
|
124
|
+
})]
|
|
125
|
+
}) : null, T.length > 0 ? /* @__PURE__ */ h("div", {
|
|
126
|
+
className: "space-y-2",
|
|
127
|
+
children: [/* @__PURE__ */ m("h4", {
|
|
128
|
+
className: "text-xs font-medium uppercase tracking-wider text-text-tertiary",
|
|
129
|
+
children: "Public links"
|
|
130
|
+
}), T.map((e) => /* @__PURE__ */ h("div", {
|
|
131
|
+
"data-testid": "share-row",
|
|
132
|
+
className: "flex items-center justify-between gap-3 rounded-md border border-border-subtle bg-bg-surface px-3 py-2",
|
|
133
|
+
children: [/* @__PURE__ */ h("div", {
|
|
134
|
+
className: "flex min-w-0 flex-1 items-center gap-2",
|
|
135
|
+
children: [
|
|
136
|
+
/* @__PURE__ */ m(f, { className: "size-4 shrink-0 text-action-primary-text" }),
|
|
137
|
+
/* @__PURE__ */ m("span", {
|
|
138
|
+
className: "truncate text-sm text-text-primary",
|
|
139
|
+
children: e.shareUrl ? s(e.shareUrl) : "Public link"
|
|
140
|
+
}),
|
|
141
|
+
e.expiresAt ? /* @__PURE__ */ h("span", {
|
|
142
|
+
className: "inline-flex items-center gap-1 whitespace-nowrap text-xs text-text-secondary",
|
|
143
|
+
children: [/* @__PURE__ */ m(u, { className: "size-3" }), _(e.expiresAt)]
|
|
144
|
+
}) : null
|
|
145
|
+
]
|
|
146
|
+
}), /* @__PURE__ */ h("div", {
|
|
147
|
+
className: "flex items-center gap-1",
|
|
148
|
+
children: [e.shareUrl ? /* @__PURE__ */ m(t, {
|
|
149
|
+
type: "button",
|
|
150
|
+
variant: "ghost",
|
|
151
|
+
size: "icon-sm",
|
|
152
|
+
"data-testid": "share-copy-link",
|
|
153
|
+
"aria-label": "Copy link",
|
|
154
|
+
onClick: () => {
|
|
155
|
+
A(e);
|
|
156
|
+
},
|
|
157
|
+
className: "text-text-secondary hover:text-action-primary-text",
|
|
158
|
+
children: D === e.id ? /* @__PURE__ */ m(l, { className: "size-4 text-status-success-text" }) : /* @__PURE__ */ m(d, { className: "size-4" })
|
|
159
|
+
}) : null, y.revokeLink ? /* @__PURE__ */ m(t, {
|
|
160
|
+
type: "button",
|
|
161
|
+
variant: "ghost",
|
|
162
|
+
size: "icon-sm",
|
|
163
|
+
"data-testid": "share-row-revoke",
|
|
164
|
+
"aria-label": "Revoke link",
|
|
165
|
+
onClick: () => {
|
|
166
|
+
y.revokeLink?.(e.id);
|
|
167
|
+
},
|
|
168
|
+
className: "text-text-secondary hover:text-status-error-text",
|
|
169
|
+
children: /* @__PURE__ */ m(p, { className: "size-4" })
|
|
170
|
+
}) : null]
|
|
171
|
+
})]
|
|
172
|
+
}, e.id))]
|
|
173
|
+
}) : null]
|
|
174
|
+
}) : /* @__PURE__ */ m("div", {
|
|
175
|
+
className: e("rounded-md border border-border-subtle bg-bg-sunken px-3 py-4", C),
|
|
176
|
+
"data-testid": "shared-with-list",
|
|
177
|
+
children: /* @__PURE__ */ m("p", {
|
|
178
|
+
className: "text-center text-sm text-text-secondary",
|
|
179
|
+
children: "No one has access yet."
|
|
180
|
+
})
|
|
181
|
+
});
|
|
182
|
+
};
|
|
183
|
+
//#endregion
|
|
184
|
+
export { y as SharedWithList };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { ROLE_DESCRIPTIONS, ROLE_LABELS, SHARE_ROLES, roleDescription, roleLabel, type ShareCapabilities, type ShareController, type ShareGrantView, type ShareResourceRef, type ShareRole, type ShareStatus, type ShareVisibility, } from './types';
|
|
2
|
+
export { ShareDialog, type ShareDialogProps } from './ShareDialog';
|
|
3
|
+
export { MemberPicker, type MemberPickerProps, type SelectedMember } from './MemberPicker';
|
|
4
|
+
export { PermissionSelect, type PermissionSelectProps } from './PermissionSelect';
|
|
5
|
+
export { SharedWithList, type SharedWithListProps } from './SharedWithList';
|
|
6
|
+
export { AcceptSharePage, type AcceptSharePageProps, type SharePreview } from './AcceptSharePage';
|
|
7
|
+
export { useWorkspaceDirectory, type UseWorkspaceDirectoryParams, type UseWorkspaceDirectoryResult, type WorkspaceDirectoryMember, } from './useWorkspaceDirectory';
|
|
8
|
+
export { copyTextToClipboard, escapeHtmlAttribute, normalizeShareUrl } from './shareUrl';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sharing/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,iBAAiB,EACjB,WAAW,EACX,WAAW,EACX,eAAe,EACf,SAAS,EACT,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,eAAe,GACrB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC3F,OAAO,EAAE,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAClF,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAGlG,OAAO,EACL,qBAAqB,EACrB,KAAK,2BAA2B,EAChC,KAAK,2BAA2B,EAChC,KAAK,wBAAwB,GAC9B,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Share-URL helpers, promoted from the VibeControls MFE so every product can
|
|
3
|
+
* normalize/copy share links consistently. Generic: the allowlist covers
|
|
4
|
+
* product-specific share prefixes plus the generic `/share/accept/` route.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Rewrite a same-surface share URL onto the current window origin. Only paths
|
|
8
|
+
* in {@link SHARE_PATH_ALLOWLIST} are rewritten; everything else (and any
|
|
9
|
+
* non-http(s) protocol, or SSR with no `window`) is returned verbatim.
|
|
10
|
+
*/
|
|
11
|
+
export declare function normalizeShareUrl(url: string): string;
|
|
12
|
+
/** Escape a string for safe interpolation into an HTML attribute value. */
|
|
13
|
+
export declare function escapeHtmlAttribute(value: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Copy text to the clipboard with a robust fallback. Prefers the async
|
|
16
|
+
* Clipboard API; falls back to a hidden `<textarea>` + `execCommand('copy')`
|
|
17
|
+
* for older browsers / insecure contexts. Rejects if the copy fails.
|
|
18
|
+
*/
|
|
19
|
+
export declare function copyTextToClipboard(text: string): Promise<void>;
|
|
20
|
+
//# sourceMappingURL=shareUrl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shareUrl.d.ts","sourceRoot":"","sources":["../../src/sharing/shareUrl.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AASH;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAcrD;AAED,2EAA2E;AAC3E,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOzD;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyBrE"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
//#region src/sharing/shareUrl.ts
|
|
2
|
+
var e = [
|
|
3
|
+
"/share/accept/",
|
|
4
|
+
"/vibecontrols/",
|
|
5
|
+
"/files/share/"
|
|
6
|
+
];
|
|
7
|
+
function t(t) {
|
|
8
|
+
if (typeof window > "u") return t;
|
|
9
|
+
try {
|
|
10
|
+
let n = new URL(t, window.location.origin);
|
|
11
|
+
if ((n.protocol === "http:" || n.protocol === "https:") && e.some((e) => n.pathname.startsWith(e))) return `${window.location.origin}${n.pathname}${n.search}${n.hash}`;
|
|
12
|
+
} catch {
|
|
13
|
+
return t;
|
|
14
|
+
}
|
|
15
|
+
return t;
|
|
16
|
+
}
|
|
17
|
+
function n(e) {
|
|
18
|
+
return e.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
19
|
+
}
|
|
20
|
+
async function r(e) {
|
|
21
|
+
try {
|
|
22
|
+
if (typeof navigator < "u" && navigator.clipboard) {
|
|
23
|
+
await navigator.clipboard.writeText(e);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
throw Error("Clipboard API unavailable");
|
|
27
|
+
} catch {
|
|
28
|
+
if (typeof document > "u") throw Error("Clipboard copy failed");
|
|
29
|
+
let t = document.createElement("textarea");
|
|
30
|
+
t.value = e, t.style.position = "fixed", t.style.opacity = "0", document.body.appendChild(t);
|
|
31
|
+
try {
|
|
32
|
+
if (t.select(), !document.execCommand("copy")) throw Error("Clipboard copy failed");
|
|
33
|
+
} finally {
|
|
34
|
+
document.body.removeChild(t);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
export { r as copyTextToClipboard, n as escapeHtmlAttribute, t as normalizeShareUrl };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic sharing binding contract.
|
|
3
|
+
*
|
|
4
|
+
* These types are the *load-bearing* contract between the presentational
|
|
5
|
+
* sharing UI in `@burdenoff/fe-libs/sharing` and the consuming microfrontend.
|
|
6
|
+
* The fe-libs components are presentational + state-machine only — they never
|
|
7
|
+
* import GraphQL codegen hooks. Each MFE implements a {@link ShareController}
|
|
8
|
+
* (wiring its own queries/mutations) and injects it into the components.
|
|
9
|
+
*/
|
|
10
|
+
/** Capability/permission level a grant confers on the grantee. */
|
|
11
|
+
export type ShareRole = 'VIEWER' | 'COMMENTER' | 'EDITOR' | 'MANAGER';
|
|
12
|
+
/** Visibility mode of the shared resource. */
|
|
13
|
+
export type ShareVisibility = 'PRIVATE' | 'SHARED_WITH_USERS' | 'SHARED_WITH_VIBE' | 'PUBLIC_LINK';
|
|
14
|
+
/** Lifecycle status of an individual grant. */
|
|
15
|
+
export type ShareStatus = 'PENDING' | 'ACCEPTED' | 'REVOKED' | 'EXPIRED';
|
|
16
|
+
/**
|
|
17
|
+
* Describes the resource being shared. `type` is an opaque, product-defined
|
|
18
|
+
* string (e.g. `"file"`, `"vibe"`, `"session"`); the UI only uses it for
|
|
19
|
+
* display + copy. `vibeId` gates the "Everyone with Vibe" mode.
|
|
20
|
+
*/
|
|
21
|
+
export interface ShareResourceRef {
|
|
22
|
+
type: string;
|
|
23
|
+
id: string;
|
|
24
|
+
name: string;
|
|
25
|
+
vibeId?: string | null;
|
|
26
|
+
ownerId?: string | null;
|
|
27
|
+
visibility?: ShareVisibility;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A single share grant (a user invite or a public link), as projected for
|
|
31
|
+
* the UI. The controller maps its own backend shape onto this view.
|
|
32
|
+
*/
|
|
33
|
+
export interface ShareGrantView {
|
|
34
|
+
id: string;
|
|
35
|
+
granteeUserId?: string | null;
|
|
36
|
+
granteeEmail?: string | null;
|
|
37
|
+
role: ShareRole;
|
|
38
|
+
status: ShareStatus;
|
|
39
|
+
visibility: ShareVisibility;
|
|
40
|
+
expiresAt?: string | null;
|
|
41
|
+
token?: string | null;
|
|
42
|
+
shareUrl?: string | null;
|
|
43
|
+
createdAt?: string;
|
|
44
|
+
}
|
|
45
|
+
/** Roles + descriptions surfaced in selectors. */
|
|
46
|
+
export interface ShareCapabilities {
|
|
47
|
+
supportsUsers: boolean;
|
|
48
|
+
supportsLink: boolean;
|
|
49
|
+
supportsVibeShare: boolean;
|
|
50
|
+
supportsExpiry: boolean;
|
|
51
|
+
roles: ShareRole[];
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The controller contract. The MFE implements this (typically by wrapping its
|
|
55
|
+
* generated GraphQL hooks) and passes it to {@link ShareDialog} /
|
|
56
|
+
* {@link SharedWithList}. Only `loading`, `grants`, `links`, `refetch`,
|
|
57
|
+
* `createUserShare`, and `capabilities` are required; the rest are optional
|
|
58
|
+
* and the UI feature-detects them (hiding/disabling controls when absent).
|
|
59
|
+
*/
|
|
60
|
+
export interface ShareController {
|
|
61
|
+
loading: boolean;
|
|
62
|
+
grants: ShareGrantView[];
|
|
63
|
+
links: ShareGrantView[];
|
|
64
|
+
refetch: () => void;
|
|
65
|
+
createUserShare(input: {
|
|
66
|
+
email: string;
|
|
67
|
+
role: ShareRole;
|
|
68
|
+
message?: string;
|
|
69
|
+
expiresAt?: string;
|
|
70
|
+
}): Promise<void>;
|
|
71
|
+
createLinkShare?(input: {
|
|
72
|
+
role: ShareRole;
|
|
73
|
+
expiresAt?: string | null;
|
|
74
|
+
}): Promise<{
|
|
75
|
+
shareUrl: string;
|
|
76
|
+
}>;
|
|
77
|
+
updateRole?(grantId: string, role: ShareRole): Promise<void>;
|
|
78
|
+
revoke?(grantId: string): Promise<void>;
|
|
79
|
+
revokeLink?(grantId: string): Promise<void>;
|
|
80
|
+
setVisibility?(visibility: ShareVisibility): Promise<void>;
|
|
81
|
+
currentVisibility?: ShareVisibility;
|
|
82
|
+
capabilities: ShareCapabilities;
|
|
83
|
+
}
|
|
84
|
+
/** Canonical, ordered list of roles (low → high privilege). */
|
|
85
|
+
export declare const SHARE_ROLES: readonly ShareRole[];
|
|
86
|
+
/** Human-friendly label per role. */
|
|
87
|
+
export declare const ROLE_LABELS: Record<ShareRole, string>;
|
|
88
|
+
/** One-line description of what each role can do. */
|
|
89
|
+
export declare const ROLE_DESCRIPTIONS: Record<ShareRole, string>;
|
|
90
|
+
/** Resolve a display label for a role, falling back to the raw value. */
|
|
91
|
+
export declare function roleLabel(role: ShareRole): string;
|
|
92
|
+
/** Resolve a description for a role, falling back to an empty string. */
|
|
93
|
+
export declare function roleDescription(role: ShareRole): string;
|
|
94
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sharing/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/sharing/types.ts
|
|
2
|
+
var e = [
|
|
3
|
+
"VIEWER",
|
|
4
|
+
"COMMENTER",
|
|
5
|
+
"EDITOR",
|
|
6
|
+
"MANAGER"
|
|
7
|
+
], t = {
|
|
8
|
+
VIEWER: "Viewer",
|
|
9
|
+
COMMENTER: "Commenter",
|
|
10
|
+
EDITOR: "Editor",
|
|
11
|
+
MANAGER: "Manager"
|
|
12
|
+
}, n = {
|
|
13
|
+
VIEWER: "Can view",
|
|
14
|
+
COMMENTER: "Can view and comment",
|
|
15
|
+
EDITOR: "Can view, comment, and edit",
|
|
16
|
+
MANAGER: "Full access, including sharing and settings"
|
|
17
|
+
};
|
|
18
|
+
function r(e) {
|
|
19
|
+
return t[e] ?? e;
|
|
20
|
+
}
|
|
21
|
+
function i(e) {
|
|
22
|
+
return n[e] ?? "";
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { n as ROLE_DESCRIPTIONS, t as ROLE_LABELS, e as SHARE_ROLES, i as roleDescription, r as roleLabel };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A workspace member entry, resolved to a human-friendly identity for the
|
|
3
|
+
* sharing autocomplete. `email` may be empty when the directory only exposes a
|
|
4
|
+
* userId (the picker still allows free-typing an email to invite).
|
|
5
|
+
*/
|
|
6
|
+
export interface WorkspaceDirectoryMember {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
email: string;
|
|
10
|
+
avatar?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface UseWorkspaceDirectoryParams {
|
|
13
|
+
/** Workspace whose members are listed. */
|
|
14
|
+
workspaceId: string;
|
|
15
|
+
/** Override the active profile's access token. */
|
|
16
|
+
authToken?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Override the WORKSPACE gateway base URL. Resolved like `useActorProfiles`:
|
|
19
|
+
* an explicit workspace gateway is honored, otherwise the URL is derived from
|
|
20
|
+
* the current hostname (LOCAL/ALPHA/PROD).
|
|
21
|
+
*/
|
|
22
|
+
apiGatewayUrl?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface UseWorkspaceDirectoryResult {
|
|
25
|
+
members: WorkspaceDirectoryMember[];
|
|
26
|
+
/** Update the case-insensitive client-side filter query. */
|
|
27
|
+
search: (q: string) => void;
|
|
28
|
+
loading: boolean;
|
|
29
|
+
/** Whether the backend reported more members beyond the loaded page. */
|
|
30
|
+
hasMore: boolean;
|
|
31
|
+
/** Load the next page of members (no-op while loading or when no more). */
|
|
32
|
+
fetchMore: () => void;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Autocomplete data source for the sharing MemberPicker. Mirrors the resolution
|
|
36
|
+
* strategy of `useActorProfiles`: it lists `workspaceMembers` (ids only) and
|
|
37
|
+
* resolves human identities via `workspaceMemberIdentities`, against the
|
|
38
|
+
* WORKSPACE gateway with a workspace token attached. Identity-resolution
|
|
39
|
+
* failures are best-effort and fall back to a short-id label rather than
|
|
40
|
+
* surfacing an error toast — the picker still works for free-typed emails.
|
|
41
|
+
*/
|
|
42
|
+
export declare function useWorkspaceDirectory(params: UseWorkspaceDirectoryParams): UseWorkspaceDirectoryResult;
|
|
43
|
+
//# sourceMappingURL=useWorkspaceDirectory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useWorkspaceDirectory.d.ts","sourceRoot":"","sources":["../../src/sharing/useWorkspaceDirectory.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,2BAA2B;IAC1C,0CAA0C;IAC1C,WAAW,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,wBAAwB,EAAE,CAAC;IACpC,4DAA4D;IAC5D,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,wEAAwE;IACxE,OAAO,EAAE,OAAO,CAAC;IACjB,2EAA2E;IAC3E,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAsDD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,2BAA2B,GAClC,2BAA2B,CA4J7B"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { resolveGatewayUrls as e } from "../shared/config/gatewayUrls.js";
|
|
2
|
+
import { getStoredAuthToken as t, graphqlFetch as n } from "../shared/graphql/fetch.js";
|
|
3
|
+
import { useCallback as r, useEffect as i, useMemo as a, useRef as o, useState as s } from "react";
|
|
4
|
+
//#region src/sharing/useWorkspaceDirectory.ts
|
|
5
|
+
var c = 50, l = "\n query SharingWorkspaceMembers($workspaceId: ID!, $pagination: PaginationInput) {\n workspaceMembers(workspaceId: $workspaceId, pagination: $pagination) {\n items {\n id\n userId\n }\n total\n hasMore\n }\n }\n", u = "\n query SharingWorkspaceMemberIdentities($userIds: [ID!]!, $workspaceId: ID) {\n workspaceMemberIdentities(userIds: $userIds, workspaceId: $workspaceId) {\n userId\n displayName\n email\n avatar\n }\n }\n";
|
|
6
|
+
function d(e) {
|
|
7
|
+
return e.length <= 12 ? e : `${e.slice(0, 8)}…${e.slice(-4)}`;
|
|
8
|
+
}
|
|
9
|
+
function f(e) {
|
|
10
|
+
return /(?:gw-wspace|graphqlworkspaces|workspaces\.)/.test(e);
|
|
11
|
+
}
|
|
12
|
+
function p(p) {
|
|
13
|
+
let { workspaceId: m, authToken: h, apiGatewayUrl: g } = p, _ = h ?? t() ?? void 0, v = a(() => {
|
|
14
|
+
try {
|
|
15
|
+
let t = e();
|
|
16
|
+
return g && f(g) ? g : t.workspaceBaseUrl;
|
|
17
|
+
} catch {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
}, [g]), [y, b] = s([]), [x, S] = s(!1), [C, w] = s(!1), [T, E] = s(""), [D, O] = s(0), k = `${m}|${v ?? ""}`, A = o(k);
|
|
21
|
+
A.current !== k && (A.current = k), i(() => {
|
|
22
|
+
b([]), w(!1), O(0);
|
|
23
|
+
}, [k]), i(() => {
|
|
24
|
+
if (!m || !v) return;
|
|
25
|
+
let e = !1;
|
|
26
|
+
async function t() {
|
|
27
|
+
S(!0);
|
|
28
|
+
try {
|
|
29
|
+
let t = await n({
|
|
30
|
+
gateway: "workspace",
|
|
31
|
+
baseUrl: v,
|
|
32
|
+
authToken: _,
|
|
33
|
+
workspaceId: m,
|
|
34
|
+
workspaceToken: !0,
|
|
35
|
+
query: l,
|
|
36
|
+
operationName: "SharingWorkspaceMembers",
|
|
37
|
+
suppressGlobalErrorEvent: !0,
|
|
38
|
+
variables: {
|
|
39
|
+
workspaceId: m,
|
|
40
|
+
pagination: {
|
|
41
|
+
limit: c,
|
|
42
|
+
offset: D * c
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
if (e) return;
|
|
47
|
+
let r = t.data?.workspaceMembers, i = r?.items ?? [], a = [...new Set(i.map((e) => e.userId))], o = /* @__PURE__ */ new Map();
|
|
48
|
+
if (a.length > 0) try {
|
|
49
|
+
let t = await n({
|
|
50
|
+
gateway: "workspace",
|
|
51
|
+
baseUrl: v,
|
|
52
|
+
authToken: _,
|
|
53
|
+
workspaceId: m,
|
|
54
|
+
workspaceToken: !0,
|
|
55
|
+
query: u,
|
|
56
|
+
operationName: "SharingWorkspaceMemberIdentities",
|
|
57
|
+
suppressGlobalErrorEvent: !0,
|
|
58
|
+
variables: {
|
|
59
|
+
userIds: a,
|
|
60
|
+
workspaceId: m
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
if (e) return;
|
|
64
|
+
for (let e of t.data?.workspaceMemberIdentities ?? []) o.set(e.userId, {
|
|
65
|
+
displayName: e.displayName,
|
|
66
|
+
email: e.email,
|
|
67
|
+
avatar: e.avatar
|
|
68
|
+
});
|
|
69
|
+
} catch {}
|
|
70
|
+
let s = a.map((e) => {
|
|
71
|
+
let t = o.get(e);
|
|
72
|
+
return {
|
|
73
|
+
id: e,
|
|
74
|
+
name: t?.displayName?.trim() || t?.email?.trim() || d(e),
|
|
75
|
+
email: t?.email?.trim() ?? "",
|
|
76
|
+
avatar: t?.avatar ?? void 0
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
if (e) return;
|
|
80
|
+
b((e) => {
|
|
81
|
+
if (D === 0) return s;
|
|
82
|
+
let t = new Set(e.map((e) => e.id));
|
|
83
|
+
return [...e, ...s.filter((e) => !t.has(e.id))];
|
|
84
|
+
}), w(!!r?.hasMore);
|
|
85
|
+
} catch {
|
|
86
|
+
e || w(!1);
|
|
87
|
+
} finally {
|
|
88
|
+
e || S(!1);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return t(), () => {
|
|
92
|
+
e = !0;
|
|
93
|
+
};
|
|
94
|
+
}, [
|
|
95
|
+
m,
|
|
96
|
+
v,
|
|
97
|
+
_,
|
|
98
|
+
D
|
|
99
|
+
]);
|
|
100
|
+
let j = r((e) => {
|
|
101
|
+
E(e);
|
|
102
|
+
}, []), M = r(() => {
|
|
103
|
+
S((e) => (!e && C && O((e) => e + 1), e));
|
|
104
|
+
}, [C]);
|
|
105
|
+
return {
|
|
106
|
+
members: a(() => {
|
|
107
|
+
let e = T.trim().toLowerCase();
|
|
108
|
+
return e ? y.filter((t) => t.name.toLowerCase().includes(e) || t.email.toLowerCase().includes(e)) : y;
|
|
109
|
+
}, [y, T]),
|
|
110
|
+
search: j,
|
|
111
|
+
loading: x,
|
|
112
|
+
hasMore: C,
|
|
113
|
+
fetchMore: M
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
export { p as useWorkspaceDirectory };
|
package/dist/sharing.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ROLE_DESCRIPTIONS as e, ROLE_LABELS as t, SHARE_ROLES as n, roleDescription as r, roleLabel as i } from "./sharing/types.js";
|
|
2
|
+
import { useWorkspaceDirectory as a } from "./sharing/useWorkspaceDirectory.js";
|
|
3
|
+
import { MemberPicker as o } from "./sharing/MemberPicker.js";
|
|
4
|
+
import { PermissionSelect as s } from "./sharing/PermissionSelect.js";
|
|
5
|
+
import { copyTextToClipboard as c, escapeHtmlAttribute as l, normalizeShareUrl as u } from "./sharing/shareUrl.js";
|
|
6
|
+
import { SharedWithList as d } from "./sharing/SharedWithList.js";
|
|
7
|
+
import { ShareDialog as f } from "./sharing/ShareDialog.js";
|
|
8
|
+
import { AcceptSharePage as p } from "./sharing/AcceptSharePage.js";
|
|
9
|
+
export { p as AcceptSharePage, o as MemberPicker, s as PermissionSelect, e as ROLE_DESCRIPTIONS, t as ROLE_LABELS, n as SHARE_ROLES, f as ShareDialog, d as SharedWithList, c as copyTextToClipboard, l as escapeHtmlAttribute, u as normalizeShareUrl, r as roleDescription, i as roleLabel, a as useWorkspaceDirectory };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@burdenoff/fe-libs",
|
|
3
|
-
"version": "2026.601.
|
|
3
|
+
"version": "2026.601.13",
|
|
4
4
|
"description": "Burdenoff frontend primitives and domain libraries",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"imports": {
|
|
@@ -22,6 +22,10 @@
|
|
|
22
22
|
"types": "./dist/ui.d.ts",
|
|
23
23
|
"import": "./dist/ui.js"
|
|
24
24
|
},
|
|
25
|
+
"./sharing": {
|
|
26
|
+
"types": "./dist/sharing.d.ts",
|
|
27
|
+
"import": "./dist/sharing.js"
|
|
28
|
+
},
|
|
25
29
|
"./chrome": {
|
|
26
30
|
"types": "./dist/chrome.d.ts",
|
|
27
31
|
"import": "./dist/chrome.js"
|