review-lens-react 0.1.0 → 0.1.1
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/review-lens-react.js +398 -278
- package/dist/review-lens-react.umd.cjs +1 -1
- package/dist/styles.css +1 -1
- package/dist/types.d.ts +12 -0
- package/package.json +1 -1
|
@@ -1,93 +1,93 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { createContext as
|
|
3
|
-
const
|
|
1
|
+
import { jsx as a, jsxs as S, Fragment as G } from "react/jsx-runtime";
|
|
2
|
+
import { createContext as q, useMemo as $, useState as N, useCallback as F, useEffect as P, useContext as H } from "react";
|
|
3
|
+
const D = [
|
|
4
4
|
"https://www.googleapis.com/auth/spreadsheets",
|
|
5
5
|
"https://www.googleapis.com/auth/userinfo.email"
|
|
6
|
-
].join(" "),
|
|
6
|
+
].join(" "), Q = "https://www.googleapis.com/oauth2/v3/userinfo";
|
|
7
7
|
function X(e) {
|
|
8
|
-
const
|
|
9
|
-
let
|
|
10
|
-
async function
|
|
11
|
-
return
|
|
8
|
+
const o = e.feedbackSheetName ?? "Feedback", t = e.usersSheetName ?? "Users";
|
|
9
|
+
let n, r;
|
|
10
|
+
async function i() {
|
|
11
|
+
return n ?? (n = te(e.googleClientId)), n;
|
|
12
12
|
}
|
|
13
|
-
async function
|
|
14
|
-
const
|
|
15
|
-
`https://sheets.googleapis.com/v4/spreadsheets/${e.spreadsheetId}${
|
|
13
|
+
async function m(d, c) {
|
|
14
|
+
const s = await i(), l = await fetch(
|
|
15
|
+
`https://sheets.googleapis.com/v4/spreadsheets/${e.spreadsheetId}${d}`,
|
|
16
16
|
{
|
|
17
|
-
...
|
|
17
|
+
...c,
|
|
18
18
|
headers: {
|
|
19
|
-
Authorization: `Bearer ${
|
|
19
|
+
Authorization: `Bearer ${s}`,
|
|
20
20
|
"Content-Type": "application/json",
|
|
21
|
-
...
|
|
21
|
+
...c == null ? void 0 : c.headers
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
);
|
|
25
|
-
if (!
|
|
26
|
-
throw new Error(`Google Sheets request failed with ${
|
|
27
|
-
return
|
|
25
|
+
if (!l.ok)
|
|
26
|
+
throw new Error(`Google Sheets request failed with ${l.status}`);
|
|
27
|
+
return l.json();
|
|
28
28
|
}
|
|
29
|
-
async function
|
|
30
|
-
return (await
|
|
31
|
-
`/values/${encodeURIComponent(
|
|
29
|
+
async function v(d) {
|
|
30
|
+
return (await m(
|
|
31
|
+
`/values/${encodeURIComponent(d)}`
|
|
32
32
|
)).values ?? [];
|
|
33
33
|
}
|
|
34
34
|
return {
|
|
35
35
|
async getCurrentUser() {
|
|
36
|
-
if (!
|
|
37
|
-
const
|
|
38
|
-
headers: { Authorization: `Bearer ${
|
|
36
|
+
if (!r) {
|
|
37
|
+
const d = await i(), c = await fetch(Q, {
|
|
38
|
+
headers: { Authorization: `Bearer ${d}` }
|
|
39
39
|
});
|
|
40
|
-
if (!
|
|
41
|
-
throw new Error(`Google userinfo request failed with ${
|
|
42
|
-
|
|
40
|
+
if (!c.ok)
|
|
41
|
+
throw new Error(`Google userinfo request failed with ${c.status}`);
|
|
42
|
+
r = (await c.json()).email;
|
|
43
43
|
}
|
|
44
|
-
if (!
|
|
44
|
+
if (!r)
|
|
45
45
|
throw new Error("Google account did not return an email address");
|
|
46
|
-
return { email:
|
|
46
|
+
return { email: r };
|
|
47
47
|
},
|
|
48
|
-
async getPermissions(
|
|
49
|
-
const [{ email:
|
|
48
|
+
async getPermissions(d) {
|
|
49
|
+
const [{ email: c }, s] = await Promise.all([this.getCurrentUser(), v(t)]), l = W(s), k = c.toLowerCase(), h = l.find(
|
|
50
50
|
(f) => {
|
|
51
51
|
var p;
|
|
52
|
-
return ((p = f.email) == null ? void 0 : p.toLowerCase()) ===
|
|
52
|
+
return ((p = f.email) == null ? void 0 : p.toLowerCase()) === k && f.active !== "false" && (!f.projectKey || f.projectKey === d);
|
|
53
53
|
}
|
|
54
54
|
);
|
|
55
|
-
return
|
|
55
|
+
return ee((h == null ? void 0 : h.role) ?? "designer");
|
|
56
56
|
},
|
|
57
|
-
async listFeedback(
|
|
58
|
-
return
|
|
59
|
-
(
|
|
60
|
-
).sort((
|
|
57
|
+
async listFeedback(d) {
|
|
58
|
+
return W(await v(o)).map(j).filter((s) => s !== null).filter(
|
|
59
|
+
(s) => s.projectKey === d.projectKey && s.contentId === d.contentId && s.normalizedPath === d.normalizedPath
|
|
60
|
+
).sort((s, l) => l.createdAt.localeCompare(s.createdAt));
|
|
61
61
|
},
|
|
62
|
-
async createFeedback(
|
|
63
|
-
const
|
|
64
|
-
...
|
|
62
|
+
async createFeedback(d) {
|
|
63
|
+
const c = (/* @__PURE__ */ new Date()).toISOString(), s = {
|
|
64
|
+
...d,
|
|
65
65
|
id: crypto.randomUUID(),
|
|
66
66
|
status: "open",
|
|
67
|
-
createdAt:
|
|
68
|
-
updatedAt:
|
|
67
|
+
createdAt: c,
|
|
68
|
+
updatedAt: c
|
|
69
69
|
};
|
|
70
|
-
return await
|
|
70
|
+
return await m(`/values/${encodeURIComponent(o)}:append?valueInputOption=RAW`, {
|
|
71
71
|
method: "POST",
|
|
72
|
-
body: JSON.stringify({ values: [
|
|
73
|
-
}),
|
|
72
|
+
body: JSON.stringify({ values: [V(s)] })
|
|
73
|
+
}), s;
|
|
74
74
|
},
|
|
75
|
-
async resolveFeedback(
|
|
76
|
-
const
|
|
77
|
-
if (
|
|
78
|
-
throw new Error(`Feedback ${
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
`/values/${encodeURIComponent(
|
|
75
|
+
async resolveFeedback(d, c) {
|
|
76
|
+
const s = await v(o), l = s[0] ?? Y, k = l.indexOf("id"), h = l.indexOf("status"), f = l.indexOf("updatedAt"), p = l.indexOf("resolvedAt"), w = l.indexOf("resolvedBy"), g = s.findIndex((R, x) => x > 0 && R[k] === d);
|
|
77
|
+
if (g < 1)
|
|
78
|
+
throw new Error(`Feedback ${d} was not found`);
|
|
79
|
+
const b = [...s[g]], L = (/* @__PURE__ */ new Date()).toISOString();
|
|
80
|
+
b[h] = "resolved", b[f] = L, b[p] = L, b[w] = c, await m(
|
|
81
|
+
`/values/${encodeURIComponent(o)}!A${g + 1}:Q${g + 1}?valueInputOption=RAW`,
|
|
82
82
|
{
|
|
83
83
|
method: "PUT",
|
|
84
|
-
body: JSON.stringify({ values: [
|
|
84
|
+
body: JSON.stringify({ values: [b] })
|
|
85
85
|
}
|
|
86
86
|
);
|
|
87
|
-
const
|
|
88
|
-
if (
|
|
89
|
-
throw new Error(`Feedback ${
|
|
90
|
-
return
|
|
87
|
+
const I = j(O(l, b));
|
|
88
|
+
if (!I)
|
|
89
|
+
throw new Error(`Feedback ${d} could not be parsed after resolving`);
|
|
90
|
+
return I;
|
|
91
91
|
}
|
|
92
92
|
};
|
|
93
93
|
}
|
|
@@ -109,7 +109,7 @@ const Y = [
|
|
|
109
109
|
"resolvedAt",
|
|
110
110
|
"resolvedBy"
|
|
111
111
|
];
|
|
112
|
-
function
|
|
112
|
+
function V(e) {
|
|
113
113
|
return [
|
|
114
114
|
e.id,
|
|
115
115
|
e.projectKey,
|
|
@@ -129,14 +129,14 @@ function Q(e) {
|
|
|
129
129
|
e.resolvedBy ?? ""
|
|
130
130
|
];
|
|
131
131
|
}
|
|
132
|
-
function
|
|
133
|
-
const [
|
|
134
|
-
return
|
|
132
|
+
function W(e) {
|
|
133
|
+
const [o, ...t] = e;
|
|
134
|
+
return o ? t.map((n) => O(o, n)) : [];
|
|
135
135
|
}
|
|
136
|
-
function
|
|
137
|
-
return Object.fromEntries(e.map((t,
|
|
136
|
+
function O(e, o) {
|
|
137
|
+
return Object.fromEntries(e.map((t, n) => [t, o[n] ?? ""]));
|
|
138
138
|
}
|
|
139
|
-
function
|
|
139
|
+
function j(e) {
|
|
140
140
|
return e.id ? {
|
|
141
141
|
id: e.id,
|
|
142
142
|
projectKey: e.projectKey,
|
|
@@ -145,23 +145,12 @@ function T(e) {
|
|
|
145
145
|
originalUrl: e.originalUrl,
|
|
146
146
|
selector: e.selector,
|
|
147
147
|
selectorStrategy: e.selectorStrategy === "stable-attribute" ? "stable-attribute" : "css-path",
|
|
148
|
-
elementFingerprint:
|
|
148
|
+
elementFingerprint: K(e.elementFingerprintJson, {
|
|
149
149
|
tagName: "",
|
|
150
150
|
width: 0,
|
|
151
151
|
height: 0
|
|
152
152
|
}),
|
|
153
|
-
cssSnapshot:
|
|
154
|
-
margin: "",
|
|
155
|
-
padding: "",
|
|
156
|
-
border: "",
|
|
157
|
-
fontFamily: "",
|
|
158
|
-
fontSize: "",
|
|
159
|
-
lineHeight: "",
|
|
160
|
-
color: "",
|
|
161
|
-
backgroundColor: "",
|
|
162
|
-
width: 0,
|
|
163
|
-
height: 0
|
|
164
|
-
}),
|
|
153
|
+
cssSnapshot: Z(e.cssSnapshotJson),
|
|
165
154
|
comment: e.comment,
|
|
166
155
|
status: e.status === "resolved" ? "resolved" : "open",
|
|
167
156
|
authorEmail: e.authorEmail,
|
|
@@ -171,175 +160,202 @@ function T(e) {
|
|
|
171
160
|
resolvedBy: e.resolvedBy || void 0
|
|
172
161
|
} : null;
|
|
173
162
|
}
|
|
174
|
-
function
|
|
163
|
+
function K(e, o) {
|
|
175
164
|
try {
|
|
176
|
-
return e ? JSON.parse(e) :
|
|
165
|
+
return e ? JSON.parse(e) : o;
|
|
177
166
|
} catch {
|
|
178
|
-
return
|
|
167
|
+
return o;
|
|
179
168
|
}
|
|
180
169
|
}
|
|
181
|
-
function
|
|
170
|
+
function Z(e) {
|
|
171
|
+
const o = K(e, {});
|
|
172
|
+
return {
|
|
173
|
+
margin: o.margin ?? "",
|
|
174
|
+
marginTop: o.marginTop ?? "",
|
|
175
|
+
marginRight: o.marginRight ?? "",
|
|
176
|
+
marginBottom: o.marginBottom ?? "",
|
|
177
|
+
marginLeft: o.marginLeft ?? "",
|
|
178
|
+
padding: o.padding ?? "",
|
|
179
|
+
paddingTop: o.paddingTop ?? "",
|
|
180
|
+
paddingRight: o.paddingRight ?? "",
|
|
181
|
+
paddingBottom: o.paddingBottom ?? "",
|
|
182
|
+
paddingLeft: o.paddingLeft ?? "",
|
|
183
|
+
border: o.border ?? "",
|
|
184
|
+
borderTopWidth: o.borderTopWidth ?? "",
|
|
185
|
+
borderRightWidth: o.borderRightWidth ?? "",
|
|
186
|
+
borderBottomWidth: o.borderBottomWidth ?? "",
|
|
187
|
+
borderLeftWidth: o.borderLeftWidth ?? "",
|
|
188
|
+
fontFamily: o.fontFamily ?? "",
|
|
189
|
+
fontSize: o.fontSize ?? "",
|
|
190
|
+
lineHeight: o.lineHeight ?? "",
|
|
191
|
+
color: o.color ?? "",
|
|
192
|
+
backgroundColor: o.backgroundColor ?? "",
|
|
193
|
+
width: o.width ?? 0,
|
|
194
|
+
height: o.height ?? 0
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
function ee(e) {
|
|
182
198
|
return e === "admin" ? ["create", "read", "resolve"] : e === "developer" ? ["read", "resolve"] : ["create", "read"];
|
|
183
199
|
}
|
|
184
|
-
async function
|
|
185
|
-
return await
|
|
186
|
-
var
|
|
187
|
-
const
|
|
200
|
+
async function te(e) {
|
|
201
|
+
return await oe(), new Promise((o, t) => {
|
|
202
|
+
var r;
|
|
203
|
+
const n = (r = window.google) == null ? void 0 : r.accounts.oauth2.initTokenClient({
|
|
188
204
|
client_id: e,
|
|
189
|
-
scope:
|
|
190
|
-
callback: (
|
|
191
|
-
if (
|
|
192
|
-
t(new Error(
|
|
205
|
+
scope: D,
|
|
206
|
+
callback: (i) => {
|
|
207
|
+
if (i.error || !i.access_token) {
|
|
208
|
+
t(new Error(i.error ?? "Google OAuth did not return an access token"));
|
|
193
209
|
return;
|
|
194
210
|
}
|
|
195
|
-
|
|
211
|
+
o(i.access_token);
|
|
196
212
|
}
|
|
197
213
|
});
|
|
198
|
-
|
|
214
|
+
n == null || n.requestAccessToken({ prompt: "" });
|
|
199
215
|
});
|
|
200
216
|
}
|
|
201
|
-
function
|
|
217
|
+
function oe() {
|
|
202
218
|
var e;
|
|
203
|
-
return (e = window.google) != null && e.accounts.oauth2 ? Promise.resolve() : new Promise((
|
|
204
|
-
const
|
|
219
|
+
return (e = window.google) != null && e.accounts.oauth2 ? Promise.resolve() : new Promise((o, t) => {
|
|
220
|
+
const n = document.querySelector(
|
|
205
221
|
'script[src="https://accounts.google.com/gsi/client"]'
|
|
206
222
|
);
|
|
207
|
-
if (
|
|
208
|
-
|
|
223
|
+
if (n) {
|
|
224
|
+
n.addEventListener("load", () => o(), { once: !0 }), n.addEventListener("error", () => t(new Error("Google Identity failed to load")), {
|
|
209
225
|
once: !0
|
|
210
226
|
});
|
|
211
227
|
return;
|
|
212
228
|
}
|
|
213
|
-
const
|
|
214
|
-
|
|
229
|
+
const r = document.createElement("script");
|
|
230
|
+
r.src = "https://accounts.google.com/gsi/client", r.async = !0, r.defer = !0, r.onload = () => o(), r.onerror = () => t(new Error("Google Identity failed to load")), document.head.append(r);
|
|
215
231
|
});
|
|
216
232
|
}
|
|
217
|
-
function
|
|
233
|
+
function ne(e) {
|
|
218
234
|
return new URL(e, window.location.href).pathname.replace(/\/+$/, "") || "/";
|
|
219
235
|
}
|
|
220
|
-
const
|
|
221
|
-
function
|
|
222
|
-
const t =
|
|
223
|
-
googleClientId:
|
|
224
|
-
spreadsheetId:
|
|
236
|
+
const _ = q(null);
|
|
237
|
+
function we({ config: e, children: o }) {
|
|
238
|
+
const t = $(() => e.adapter ? e.adapter : X({
|
|
239
|
+
googleClientId: U(e.googleClientId, "googleClientId"),
|
|
240
|
+
spreadsheetId: U(e.spreadsheetId, "spreadsheetId"),
|
|
225
241
|
feedbackSheetName: e.sheetName ?? "Feedback"
|
|
226
|
-
}), [e.adapter, e.googleClientId, e.sheetName, e.spreadsheetId]),
|
|
242
|
+
}), [e.adapter, e.googleClientId, e.sheetName, e.spreadsheetId]), n = e.currentUrl ?? window.location.href, r = (e.normalizeUrl ?? ne)(n), [i, m] = N(), [v, d] = N([]), [c, s] = N([]), l = F(async () => {
|
|
227
243
|
const p = await t.listFeedback({
|
|
228
244
|
projectKey: e.projectKey,
|
|
229
245
|
contentId: e.contentId,
|
|
230
|
-
normalizedPath:
|
|
246
|
+
normalizedPath: r
|
|
231
247
|
});
|
|
232
|
-
|
|
233
|
-
}, [t, e.contentId, e.projectKey,
|
|
234
|
-
|
|
248
|
+
s(p);
|
|
249
|
+
}, [t, e.contentId, e.projectKey, r]);
|
|
250
|
+
P(() => {
|
|
235
251
|
let p = !0;
|
|
236
252
|
async function w() {
|
|
237
|
-
const [
|
|
253
|
+
const [g, b] = await Promise.all([
|
|
238
254
|
t.getCurrentUser(),
|
|
239
255
|
t.getPermissions(e.projectKey)
|
|
240
256
|
]);
|
|
241
|
-
p && (
|
|
257
|
+
p && (m(g), d(b), await l());
|
|
242
258
|
}
|
|
243
259
|
return w(), () => {
|
|
244
260
|
p = !1;
|
|
245
261
|
};
|
|
246
|
-
}, [t, e.projectKey,
|
|
247
|
-
const
|
|
262
|
+
}, [t, e.projectKey, l]);
|
|
263
|
+
const k = F(
|
|
248
264
|
async (p) => {
|
|
249
265
|
const w = await t.createFeedback(p);
|
|
250
|
-
return
|
|
266
|
+
return s((g) => [w, ...g]), w;
|
|
251
267
|
},
|
|
252
268
|
[t]
|
|
253
|
-
),
|
|
269
|
+
), h = F(
|
|
254
270
|
async (p) => {
|
|
255
|
-
const w = await t.resolveFeedback(p, (
|
|
256
|
-
return
|
|
257
|
-
(
|
|
271
|
+
const w = await t.resolveFeedback(p, (i == null ? void 0 : i.email) ?? "");
|
|
272
|
+
return s(
|
|
273
|
+
(g) => g.map((b) => b.id === p ? w : b)
|
|
258
274
|
), w;
|
|
259
275
|
},
|
|
260
|
-
[t,
|
|
261
|
-
), f =
|
|
276
|
+
[t, i == null ? void 0 : i.email]
|
|
277
|
+
), f = $(
|
|
262
278
|
() => ({
|
|
263
279
|
config: e,
|
|
264
280
|
adapter: t,
|
|
265
|
-
currentUser:
|
|
266
|
-
permissions:
|
|
267
|
-
feedback:
|
|
268
|
-
normalizedPath:
|
|
269
|
-
refreshFeedback:
|
|
270
|
-
createFeedback:
|
|
271
|
-
resolveFeedback:
|
|
281
|
+
currentUser: i,
|
|
282
|
+
permissions: v,
|
|
283
|
+
feedback: c,
|
|
284
|
+
normalizedPath: r,
|
|
285
|
+
refreshFeedback: l,
|
|
286
|
+
createFeedback: k,
|
|
287
|
+
resolveFeedback: h
|
|
272
288
|
}),
|
|
273
289
|
[
|
|
274
290
|
t,
|
|
275
291
|
e,
|
|
276
|
-
|
|
277
|
-
|
|
292
|
+
k,
|
|
293
|
+
i,
|
|
294
|
+
c,
|
|
295
|
+
r,
|
|
296
|
+
v,
|
|
278
297
|
l,
|
|
279
|
-
|
|
280
|
-
b,
|
|
281
|
-
d,
|
|
282
|
-
u
|
|
298
|
+
h
|
|
283
299
|
]
|
|
284
300
|
);
|
|
285
|
-
return /* @__PURE__ */
|
|
301
|
+
return /* @__PURE__ */ a(_.Provider, { value: f, children: o });
|
|
286
302
|
}
|
|
287
|
-
function
|
|
288
|
-
const e = H(
|
|
303
|
+
function re() {
|
|
304
|
+
const e = H(_);
|
|
289
305
|
if (!e)
|
|
290
306
|
throw new Error("useReviewLens must be used inside ReviewLensProvider");
|
|
291
307
|
return e;
|
|
292
308
|
}
|
|
293
|
-
function
|
|
309
|
+
function U(e, o) {
|
|
294
310
|
if (!e)
|
|
295
|
-
throw new Error(`review-lens-react requires config.${
|
|
311
|
+
throw new Error(`review-lens-react requires config.${o} when no adapter is provided`);
|
|
296
312
|
return e;
|
|
297
313
|
}
|
|
298
|
-
const
|
|
314
|
+
const ie = [
|
|
299
315
|
"data-review-id",
|
|
300
316
|
"data-testid",
|
|
301
317
|
"data-test-id",
|
|
302
318
|
"aria-label",
|
|
303
319
|
"name"
|
|
304
320
|
];
|
|
305
|
-
function
|
|
306
|
-
const
|
|
321
|
+
function M(e) {
|
|
322
|
+
const o = e.getBoundingClientRect(), t = se(e);
|
|
307
323
|
return {
|
|
308
324
|
selector: t.selector,
|
|
309
325
|
selectorStrategy: t.strategy,
|
|
310
|
-
fingerprint:
|
|
311
|
-
cssSnapshot:
|
|
312
|
-
rect:
|
|
326
|
+
fingerprint: de(e, o),
|
|
327
|
+
cssSnapshot: ce(e, o),
|
|
328
|
+
rect: o
|
|
313
329
|
};
|
|
314
330
|
}
|
|
315
|
-
function
|
|
316
|
-
for (const
|
|
317
|
-
const t = e.getAttribute(
|
|
331
|
+
function se(e) {
|
|
332
|
+
for (const o of ie) {
|
|
333
|
+
const t = e.getAttribute(o);
|
|
318
334
|
if (t)
|
|
319
335
|
return {
|
|
320
|
-
selector: `[${
|
|
336
|
+
selector: `[${o}="${z(t)}"]`,
|
|
321
337
|
strategy: "stable-attribute"
|
|
322
338
|
};
|
|
323
339
|
}
|
|
324
|
-
return e.id ? { selector: `#${
|
|
340
|
+
return e.id ? { selector: `#${z(e.id)}`, strategy: "stable-attribute" } : { selector: ae(e), strategy: "css-path" };
|
|
325
341
|
}
|
|
326
|
-
function
|
|
327
|
-
const
|
|
342
|
+
function ae(e) {
|
|
343
|
+
const o = [];
|
|
328
344
|
let t = e;
|
|
329
345
|
for (; t && t.nodeType === Node.ELEMENT_NODE && t !== document.body; ) {
|
|
330
|
-
const
|
|
331
|
-
if (!
|
|
332
|
-
|
|
346
|
+
const n = t.parentElement, r = t.tagName.toLowerCase();
|
|
347
|
+
if (!n) {
|
|
348
|
+
o.unshift(r);
|
|
333
349
|
break;
|
|
334
350
|
}
|
|
335
|
-
const
|
|
336
|
-
(
|
|
337
|
-
),
|
|
338
|
-
|
|
351
|
+
const i = t.tagName, m = Array.from(n.children).filter(
|
|
352
|
+
(d) => d.tagName === i
|
|
353
|
+
), v = m.indexOf(t) + 1;
|
|
354
|
+
o.unshift(m.length > 1 ? `${r}:nth-of-type(${v})` : r), t = n;
|
|
339
355
|
}
|
|
340
|
-
return
|
|
356
|
+
return o.join(" > ");
|
|
341
357
|
}
|
|
342
|
-
function
|
|
358
|
+
function de(e, o) {
|
|
343
359
|
var t;
|
|
344
360
|
return {
|
|
345
361
|
tagName: e.tagName.toLowerCase(),
|
|
@@ -347,203 +363,259 @@ function ae(e, n) {
|
|
|
347
363
|
className: e.getAttribute("class") || void 0,
|
|
348
364
|
textSnippet: ((t = e.textContent) == null ? void 0 : t.trim().slice(0, 80)) || void 0,
|
|
349
365
|
ariaLabel: e.getAttribute("aria-label") || void 0,
|
|
350
|
-
width: Math.round(
|
|
351
|
-
height: Math.round(
|
|
366
|
+
width: Math.round(o.width),
|
|
367
|
+
height: Math.round(o.height)
|
|
352
368
|
};
|
|
353
369
|
}
|
|
354
|
-
function
|
|
370
|
+
function ce(e, o) {
|
|
355
371
|
const t = window.getComputedStyle(e);
|
|
356
372
|
return {
|
|
357
|
-
margin:
|
|
358
|
-
|
|
373
|
+
margin: A(t.marginTop, t.marginRight, t.marginBottom, t.marginLeft),
|
|
374
|
+
marginTop: t.marginTop,
|
|
375
|
+
marginRight: t.marginRight,
|
|
376
|
+
marginBottom: t.marginBottom,
|
|
377
|
+
marginLeft: t.marginLeft,
|
|
378
|
+
padding: A(
|
|
359
379
|
t.paddingTop,
|
|
360
380
|
t.paddingRight,
|
|
361
381
|
t.paddingBottom,
|
|
362
382
|
t.paddingLeft
|
|
363
383
|
),
|
|
364
|
-
|
|
384
|
+
paddingTop: t.paddingTop,
|
|
385
|
+
paddingRight: t.paddingRight,
|
|
386
|
+
paddingBottom: t.paddingBottom,
|
|
387
|
+
paddingLeft: t.paddingLeft,
|
|
388
|
+
border: A(
|
|
365
389
|
t.borderTopWidth,
|
|
366
390
|
t.borderRightWidth,
|
|
367
391
|
t.borderBottomWidth,
|
|
368
392
|
t.borderLeftWidth
|
|
369
393
|
),
|
|
394
|
+
borderTopWidth: t.borderTopWidth,
|
|
395
|
+
borderRightWidth: t.borderRightWidth,
|
|
396
|
+
borderBottomWidth: t.borderBottomWidth,
|
|
397
|
+
borderLeftWidth: t.borderLeftWidth,
|
|
370
398
|
fontFamily: t.fontFamily,
|
|
371
399
|
fontSize: t.fontSize,
|
|
372
400
|
lineHeight: t.lineHeight,
|
|
373
401
|
color: t.color,
|
|
374
402
|
backgroundColor: t.backgroundColor,
|
|
375
|
-
width: Math.round(
|
|
376
|
-
height: Math.round(
|
|
403
|
+
width: Math.round(o.width),
|
|
404
|
+
height: Math.round(o.height)
|
|
377
405
|
};
|
|
378
406
|
}
|
|
379
|
-
function
|
|
380
|
-
return e ===
|
|
407
|
+
function A(e, o, t, n) {
|
|
408
|
+
return e === o && o === t && t === n ? e : `${e} ${o} ${t} ${n}`;
|
|
381
409
|
}
|
|
382
|
-
function
|
|
410
|
+
function z(e) {
|
|
383
411
|
return typeof CSS < "u" && typeof CSS.escape == "function" ? CSS.escape(e) : e.replace(/["\\]/g, "\\$&");
|
|
384
412
|
}
|
|
385
|
-
function
|
|
413
|
+
function be({
|
|
386
414
|
open: e,
|
|
387
|
-
onOpenChange:
|
|
415
|
+
onOpenChange: o,
|
|
388
416
|
placement: t = "top-right",
|
|
389
|
-
showResolved:
|
|
417
|
+
showResolved: n = !1
|
|
390
418
|
}) {
|
|
391
419
|
const {
|
|
392
|
-
config:
|
|
393
|
-
currentUser:
|
|
394
|
-
feedback:
|
|
395
|
-
normalizedPath:
|
|
396
|
-
permissions:
|
|
397
|
-
createFeedback:
|
|
398
|
-
resolveFeedback:
|
|
399
|
-
} =
|
|
400
|
-
() =>
|
|
401
|
-
[
|
|
420
|
+
config: r,
|
|
421
|
+
currentUser: i,
|
|
422
|
+
feedback: m,
|
|
423
|
+
normalizedPath: v,
|
|
424
|
+
permissions: d,
|
|
425
|
+
createFeedback: c,
|
|
426
|
+
resolveFeedback: s
|
|
427
|
+
} = re(), [l, k] = N(), [h, f] = N(), [p, w] = N(""), [g, b] = N(), L = d.includes("create"), I = d.includes("resolve"), R = $(
|
|
428
|
+
() => m.filter((u) => n || u.status !== "resolved"),
|
|
429
|
+
[m, n]
|
|
402
430
|
);
|
|
403
|
-
|
|
404
|
-
e || (
|
|
431
|
+
P(() => {
|
|
432
|
+
e || (k(void 0), f(void 0), w(""));
|
|
405
433
|
}, [e]);
|
|
406
|
-
const
|
|
407
|
-
const
|
|
408
|
-
if (
|
|
409
|
-
return
|
|
410
|
-
const
|
|
411
|
-
return !
|
|
434
|
+
const x = F((u) => {
|
|
435
|
+
const E = u.target instanceof Element ? u.target : null;
|
|
436
|
+
if (E)
|
|
437
|
+
return E.closest("[data-review-lens-ui]") ? null : E;
|
|
438
|
+
const C = document.elementFromPoint(u.clientX, u.clientY);
|
|
439
|
+
return !C || C.closest("[data-review-lens-ui]") ? null : C;
|
|
412
440
|
}, []);
|
|
413
|
-
if (
|
|
414
|
-
if (!e ||
|
|
441
|
+
if (P(() => {
|
|
442
|
+
if (!e || h)
|
|
415
443
|
return;
|
|
416
|
-
function
|
|
417
|
-
const
|
|
418
|
-
|
|
444
|
+
function u(C) {
|
|
445
|
+
const B = x(C);
|
|
446
|
+
k(B ? M(B) : void 0);
|
|
419
447
|
}
|
|
420
|
-
function
|
|
421
|
-
const
|
|
422
|
-
|
|
448
|
+
function E(C) {
|
|
449
|
+
const B = x(C);
|
|
450
|
+
B && (C.preventDefault(), C.stopPropagation(), f(M(B)));
|
|
423
451
|
}
|
|
424
|
-
return window.addEventListener("mousemove",
|
|
425
|
-
window.removeEventListener("mousemove",
|
|
452
|
+
return window.addEventListener("mousemove", u, !0), window.addEventListener("click", E, !0), () => {
|
|
453
|
+
window.removeEventListener("mousemove", u, !0), window.removeEventListener("click", E, !0);
|
|
426
454
|
};
|
|
427
|
-
}, [
|
|
455
|
+
}, [x, h, e]), !e)
|
|
428
456
|
return null;
|
|
429
|
-
const
|
|
430
|
-
async function
|
|
431
|
-
!
|
|
432
|
-
projectKey:
|
|
433
|
-
contentId:
|
|
434
|
-
normalizedPath:
|
|
435
|
-
originalUrl:
|
|
436
|
-
selector:
|
|
437
|
-
selectorStrategy:
|
|
438
|
-
elementFingerprint:
|
|
439
|
-
cssSnapshot:
|
|
457
|
+
const T = h ?? l;
|
|
458
|
+
async function J() {
|
|
459
|
+
!h || !p.trim() || !i || !L || (await c({
|
|
460
|
+
projectKey: r.projectKey,
|
|
461
|
+
contentId: r.contentId,
|
|
462
|
+
normalizedPath: v,
|
|
463
|
+
originalUrl: r.currentUrl ?? window.location.href,
|
|
464
|
+
selector: h.selector,
|
|
465
|
+
selectorStrategy: h.selectorStrategy,
|
|
466
|
+
elementFingerprint: h.fingerprint,
|
|
467
|
+
cssSnapshot: h.cssSnapshot,
|
|
440
468
|
comment: p.trim(),
|
|
441
|
-
authorEmail:
|
|
469
|
+
authorEmail: i.email
|
|
442
470
|
}), w(""), f(void 0));
|
|
443
471
|
}
|
|
444
|
-
return /* @__PURE__ */
|
|
445
|
-
|
|
446
|
-
/* @__PURE__ */
|
|
447
|
-
|
|
472
|
+
return /* @__PURE__ */ S("div", { className: "review-lens-root", "data-review-lens-ui": !0, children: [
|
|
473
|
+
T ? /* @__PURE__ */ a(le, { target: T, locked: !!h }) : null,
|
|
474
|
+
/* @__PURE__ */ a(
|
|
475
|
+
he,
|
|
448
476
|
{
|
|
449
|
-
feedback:
|
|
450
|
-
selectedFeedback:
|
|
451
|
-
onSelect:
|
|
477
|
+
feedback: R,
|
|
478
|
+
selectedFeedback: g,
|
|
479
|
+
onSelect: b
|
|
452
480
|
}
|
|
453
481
|
),
|
|
454
|
-
/* @__PURE__ */
|
|
455
|
-
/* @__PURE__ */
|
|
456
|
-
/* @__PURE__ */
|
|
457
|
-
/* @__PURE__ */
|
|
458
|
-
/* @__PURE__ */
|
|
482
|
+
/* @__PURE__ */ S("aside", { className: `review-lens-panel review-lens-panel--${t}`, "data-review-lens-ui": !0, children: [
|
|
483
|
+
/* @__PURE__ */ S("header", { className: "review-lens-panel__header", children: [
|
|
484
|
+
/* @__PURE__ */ S("div", { children: [
|
|
485
|
+
/* @__PURE__ */ a("p", { className: "review-lens-kicker", children: "Review Lens" }),
|
|
486
|
+
/* @__PURE__ */ a("h2", { children: h ? "Element locked" : "Inspecting" })
|
|
459
487
|
] }),
|
|
460
|
-
/* @__PURE__ */
|
|
488
|
+
/* @__PURE__ */ a("button", { type: "button", onClick: () => o == null ? void 0 : o(!1), children: "Close" })
|
|
461
489
|
] }),
|
|
462
|
-
|
|
463
|
-
|
|
490
|
+
T ? /* @__PURE__ */ a(ue, { target: T }) : /* @__PURE__ */ a("p", { children: "Move over the app to inspect." }),
|
|
491
|
+
h ? /* @__PURE__ */ S(
|
|
464
492
|
"form",
|
|
465
493
|
{
|
|
466
494
|
className: "review-lens-feedback-form",
|
|
467
|
-
onSubmit: (
|
|
468
|
-
|
|
495
|
+
onSubmit: (u) => {
|
|
496
|
+
u.preventDefault(), J();
|
|
469
497
|
},
|
|
470
498
|
children: [
|
|
471
|
-
/* @__PURE__ */
|
|
472
|
-
/* @__PURE__ */
|
|
499
|
+
/* @__PURE__ */ a("label", { htmlFor: "review-lens-comment", children: "Feedback" }),
|
|
500
|
+
/* @__PURE__ */ a(
|
|
473
501
|
"textarea",
|
|
474
502
|
{
|
|
475
503
|
id: "review-lens-comment",
|
|
476
504
|
value: p,
|
|
477
|
-
disabled: !
|
|
478
|
-
onChange: (
|
|
479
|
-
placeholder:
|
|
505
|
+
disabled: !L,
|
|
506
|
+
onChange: (u) => w(u.target.value),
|
|
507
|
+
placeholder: L ? "Describe the UX issue..." : "You do not have permission to comment."
|
|
480
508
|
}
|
|
481
509
|
),
|
|
482
|
-
/* @__PURE__ */
|
|
483
|
-
/* @__PURE__ */
|
|
484
|
-
/* @__PURE__ */
|
|
510
|
+
/* @__PURE__ */ S("div", { className: "review-lens-actions", children: [
|
|
511
|
+
/* @__PURE__ */ a("button", { type: "button", onClick: () => f(void 0), children: "Unlock" }),
|
|
512
|
+
/* @__PURE__ */ a("button", { type: "submit", disabled: !p.trim() || !L, children: "Save feedback" })
|
|
485
513
|
] })
|
|
486
514
|
]
|
|
487
515
|
}
|
|
488
516
|
) : null,
|
|
489
|
-
/* @__PURE__ */
|
|
490
|
-
/* @__PURE__ */
|
|
491
|
-
|
|
492
|
-
|
|
517
|
+
/* @__PURE__ */ S("section", { className: "review-lens-comments", children: [
|
|
518
|
+
/* @__PURE__ */ a("h3", { children: "Page feedback" }),
|
|
519
|
+
R.length === 0 ? /* @__PURE__ */ a("p", { children: "No feedback for this view." }) : null,
|
|
520
|
+
R.map((u) => /* @__PURE__ */ S(
|
|
493
521
|
"article",
|
|
494
522
|
{
|
|
495
|
-
className: (
|
|
523
|
+
className: (g == null ? void 0 : g.id) === u.id ? "review-lens-comment review-lens-comment--selected" : "review-lens-comment",
|
|
496
524
|
children: [
|
|
497
|
-
/* @__PURE__ */
|
|
498
|
-
/* @__PURE__ */
|
|
499
|
-
|
|
525
|
+
/* @__PURE__ */ a("p", { children: u.comment }),
|
|
526
|
+
/* @__PURE__ */ a("span", { children: u.authorEmail }),
|
|
527
|
+
u.status === "open" && I ? /* @__PURE__ */ a("button", { type: "button", onClick: () => void s(u.id), children: "Resolve" }) : null
|
|
500
528
|
]
|
|
501
529
|
},
|
|
502
|
-
|
|
530
|
+
u.id
|
|
503
531
|
))
|
|
504
532
|
] })
|
|
505
533
|
] })
|
|
506
534
|
] });
|
|
507
535
|
}
|
|
508
|
-
function
|
|
509
|
-
|
|
536
|
+
function le({ target: e, locked: o }) {
|
|
537
|
+
const t = me(e);
|
|
538
|
+
return /* @__PURE__ */ S(
|
|
510
539
|
"div",
|
|
511
540
|
{
|
|
512
|
-
className:
|
|
541
|
+
className: o ? "review-lens-highlight review-lens-highlight--locked" : "review-lens-highlight",
|
|
513
542
|
style: {
|
|
514
|
-
top:
|
|
515
|
-
left:
|
|
516
|
-
width:
|
|
517
|
-
height:
|
|
518
|
-
}
|
|
543
|
+
top: t.margin.top,
|
|
544
|
+
left: t.margin.left,
|
|
545
|
+
width: t.margin.width,
|
|
546
|
+
height: t.margin.height
|
|
547
|
+
},
|
|
548
|
+
children: [
|
|
549
|
+
/* @__PURE__ */ a(
|
|
550
|
+
"div",
|
|
551
|
+
{
|
|
552
|
+
className: "review-lens-highlight__border",
|
|
553
|
+
style: {
|
|
554
|
+
top: t.border.top - t.margin.top,
|
|
555
|
+
left: t.border.left - t.margin.left,
|
|
556
|
+
width: t.border.width,
|
|
557
|
+
height: t.border.height
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
),
|
|
561
|
+
/* @__PURE__ */ a(
|
|
562
|
+
"div",
|
|
563
|
+
{
|
|
564
|
+
className: "review-lens-highlight__padding",
|
|
565
|
+
style: {
|
|
566
|
+
top: t.padding.top - t.margin.top,
|
|
567
|
+
left: t.padding.left - t.margin.left,
|
|
568
|
+
width: t.padding.width,
|
|
569
|
+
height: t.padding.height
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
),
|
|
573
|
+
/* @__PURE__ */ a(
|
|
574
|
+
"div",
|
|
575
|
+
{
|
|
576
|
+
className: "review-lens-highlight__content",
|
|
577
|
+
style: {
|
|
578
|
+
top: t.content.top - t.margin.top,
|
|
579
|
+
left: t.content.left - t.margin.left,
|
|
580
|
+
width: t.content.width,
|
|
581
|
+
height: t.content.height
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
),
|
|
585
|
+
/* @__PURE__ */ S("div", { className: "review-lens-highlight__label", children: [
|
|
586
|
+
Math.round(e.rect.width),
|
|
587
|
+
" x ",
|
|
588
|
+
Math.round(e.rect.height)
|
|
589
|
+
] })
|
|
590
|
+
]
|
|
519
591
|
}
|
|
520
592
|
);
|
|
521
593
|
}
|
|
522
|
-
function
|
|
594
|
+
function he({
|
|
523
595
|
feedback: e,
|
|
524
|
-
selectedFeedback:
|
|
596
|
+
selectedFeedback: o,
|
|
525
597
|
onSelect: t
|
|
526
598
|
}) {
|
|
527
|
-
return /* @__PURE__ */
|
|
528
|
-
const
|
|
529
|
-
return
|
|
599
|
+
return /* @__PURE__ */ a(G, { children: e.map((n) => {
|
|
600
|
+
const r = pe(n.selector), i = r == null ? void 0 : r.getBoundingClientRect();
|
|
601
|
+
return i ? /* @__PURE__ */ a(
|
|
530
602
|
"button",
|
|
531
603
|
{
|
|
532
604
|
type: "button",
|
|
533
|
-
className: (
|
|
605
|
+
className: (o == null ? void 0 : o.id) === n.id ? "review-lens-marker review-lens-marker--selected" : "review-lens-marker",
|
|
534
606
|
style: {
|
|
535
|
-
top:
|
|
536
|
-
left:
|
|
607
|
+
top: i.top,
|
|
608
|
+
left: i.left + i.width
|
|
537
609
|
},
|
|
538
|
-
onClick: () => t(
|
|
539
|
-
"aria-label": `Open feedback from ${
|
|
610
|
+
onClick: () => t(n),
|
|
611
|
+
"aria-label": `Open feedback from ${n.authorEmail}`
|
|
540
612
|
},
|
|
541
|
-
|
|
613
|
+
n.id
|
|
542
614
|
) : null;
|
|
543
615
|
}) });
|
|
544
616
|
}
|
|
545
|
-
function
|
|
546
|
-
const
|
|
617
|
+
function ue({ target: e }) {
|
|
618
|
+
const o = [
|
|
547
619
|
["Selector", e.selector],
|
|
548
620
|
["Size", `${e.cssSnapshot.width} x ${e.cssSnapshot.height}`],
|
|
549
621
|
["Margin", e.cssSnapshot.margin],
|
|
@@ -554,23 +626,71 @@ function de({ target: e }) {
|
|
|
554
626
|
["Color", e.cssSnapshot.color],
|
|
555
627
|
["Background", e.cssSnapshot.backgroundColor]
|
|
556
628
|
];
|
|
557
|
-
return /* @__PURE__ */
|
|
558
|
-
/* @__PURE__ */
|
|
559
|
-
/* @__PURE__ */
|
|
629
|
+
return /* @__PURE__ */ a("dl", { className: "review-lens-metrics", children: o.map(([t, n]) => /* @__PURE__ */ S("div", { children: [
|
|
630
|
+
/* @__PURE__ */ a("dt", { children: t }),
|
|
631
|
+
/* @__PURE__ */ a("dd", { children: n })
|
|
560
632
|
] }, t)) });
|
|
561
633
|
}
|
|
562
|
-
function
|
|
634
|
+
function pe(e) {
|
|
563
635
|
try {
|
|
564
636
|
return document.querySelector(e);
|
|
565
637
|
} catch {
|
|
566
638
|
return null;
|
|
567
639
|
}
|
|
568
640
|
}
|
|
641
|
+
function me(e) {
|
|
642
|
+
const o = {
|
|
643
|
+
top: y(e.cssSnapshot.marginTop),
|
|
644
|
+
right: y(e.cssSnapshot.marginRight),
|
|
645
|
+
bottom: y(e.cssSnapshot.marginBottom),
|
|
646
|
+
left: y(e.cssSnapshot.marginLeft)
|
|
647
|
+
}, t = {
|
|
648
|
+
top: y(e.cssSnapshot.borderTopWidth),
|
|
649
|
+
right: y(e.cssSnapshot.borderRightWidth),
|
|
650
|
+
bottom: y(e.cssSnapshot.borderBottomWidth),
|
|
651
|
+
left: y(e.cssSnapshot.borderLeftWidth)
|
|
652
|
+
}, n = {
|
|
653
|
+
top: y(e.cssSnapshot.paddingTop),
|
|
654
|
+
right: y(e.cssSnapshot.paddingRight),
|
|
655
|
+
bottom: y(e.cssSnapshot.paddingBottom),
|
|
656
|
+
left: y(e.cssSnapshot.paddingLeft)
|
|
657
|
+
}, r = {
|
|
658
|
+
top: e.rect.top,
|
|
659
|
+
left: e.rect.left,
|
|
660
|
+
width: Math.max(e.rect.width, 0),
|
|
661
|
+
height: Math.max(e.rect.height, 0)
|
|
662
|
+
}, i = {
|
|
663
|
+
top: r.top - o.top,
|
|
664
|
+
left: r.left - o.left,
|
|
665
|
+
width: r.width + o.left + o.right,
|
|
666
|
+
height: r.height + o.top + o.bottom
|
|
667
|
+
}, m = {
|
|
668
|
+
top: r.top + t.top,
|
|
669
|
+
left: r.left + t.left,
|
|
670
|
+
width: Math.max(r.width - t.left - t.right, 0),
|
|
671
|
+
height: Math.max(r.height - t.top - t.bottom, 0)
|
|
672
|
+
}, v = {
|
|
673
|
+
top: m.top + n.top,
|
|
674
|
+
left: m.left + n.left,
|
|
675
|
+
width: Math.max(m.width - n.left - n.right, 0),
|
|
676
|
+
height: Math.max(m.height - n.top - n.bottom, 0)
|
|
677
|
+
};
|
|
678
|
+
return {
|
|
679
|
+
margin: i,
|
|
680
|
+
border: r,
|
|
681
|
+
padding: m,
|
|
682
|
+
content: v
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
function y(e) {
|
|
686
|
+
const o = Number.parseFloat(e || "0");
|
|
687
|
+
return Number.isFinite(o) ? o : 0;
|
|
688
|
+
}
|
|
569
689
|
export {
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
690
|
+
be as ReviewLensOverlay,
|
|
691
|
+
we as ReviewLensProvider,
|
|
692
|
+
M as buildElementTarget,
|
|
573
693
|
X as createGoogleSheetsAdapter,
|
|
574
|
-
|
|
575
|
-
|
|
694
|
+
ne as normalizeReviewUrl,
|
|
695
|
+
re as useReviewLens
|
|
576
696
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(w,r){typeof exports=="object"&&typeof module<"u"?r(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],r):(w=typeof globalThis<"u"?globalThis:w||self,r(w.ReviewLensReact={},w.jsxRuntime,w.React))})(this,(function(w,r,p){"use strict";const J=["https://www.googleapis.com/auth/spreadsheets","https://www.googleapis.com/auth/userinfo.email"].join(" "),D="https://www.googleapis.com/oauth2/v3/userinfo";function T(e){const n=e.feedbackSheetName??"Feedback",t=e.usersSheetName??"Users";let o,s;async function a(){return o??(o=X(e.googleClientId)),o}async function y(c,l){const i=await a(),d=await fetch(`https://sheets.googleapis.com/v4/spreadsheets/${e.spreadsheetId}${c}`,{...l,headers:{Authorization:`Bearer ${i}`,"Content-Type":"application/json",...l==null?void 0:l.headers}});if(!d.ok)throw new Error(`Google Sheets request failed with ${d.status}`);return d.json()}async function S(c){return(await y(`/values/${encodeURIComponent(c)}`)).values??[]}return{async getCurrentUser(){if(!s){const c=await a(),l=await fetch(D,{headers:{Authorization:`Bearer ${c}`}});if(!l.ok)throw new Error(`Google userinfo request failed with ${l.status}`);s=(await l.json()).email}if(!s)throw new Error("Google account did not return an email address");return{email:s}},async getPermissions(c){const[{email:l},i]=await Promise.all([this.getCurrentUser(),S(t)]),d=U(i),k=l.toLowerCase(),u=d.find(g=>{var f;return((f=g.email)==null?void 0:f.toLowerCase())===k&&g.active!=="false"&&(!g.projectKey||g.projectKey===c)});return _((u==null?void 0:u.role)??"designer")},async listFeedback(c){return U(await S(n)).map(x).filter(i=>i!==null).filter(i=>i.projectKey===c.projectKey&&i.contentId===c.contentId&&i.normalizedPath===c.normalizedPath).sort((i,d)=>d.createdAt.localeCompare(i.createdAt))},async createFeedback(c){const l=new Date().toISOString(),i={...c,id:crypto.randomUUID(),status:"open",createdAt:l,updatedAt:l};return await y(`/values/${encodeURIComponent(n)}:append?valueInputOption=RAW`,{method:"POST",body:JSON.stringify({values:[W(i)]})}),i},async resolveFeedback(c,l){const i=await S(n),d=i[0]??H,k=d.indexOf("id"),u=d.indexOf("status"),g=d.indexOf("updatedAt"),f=d.indexOf("resolvedAt"),v=d.indexOf("resolvedBy"),m=i.findIndex((A,N)=>N>0&&A[k]===c);if(m<1)throw new Error(`Feedback ${c} was not found`);const b=[...i[m]],E=new Date().toISOString();b[u]="resolved",b[g]=E,b[f]=E,b[v]=l,await y(`/values/${encodeURIComponent(n)}!A${m+1}:Q${m+1}?valueInputOption=RAW`,{method:"PUT",body:JSON.stringify({values:[b]})});const P=x(O(d,b));if(!P)throw new Error(`Feedback ${c} could not be parsed after resolving`);return P}}}const H=["id","projectKey","contentId","normalizedPath","originalUrl","selector","selectorStrategy","elementFingerprintJson","cssSnapshotJson","comment","status","authorEmail","createdAt","updatedAt","resolvedAt","resolvedBy"];function W(e){return[e.id,e.projectKey,e.contentId,e.normalizedPath,e.originalUrl,e.selector,e.selectorStrategy,JSON.stringify(e.elementFingerprint),JSON.stringify(e.cssSnapshot),e.comment,e.status,e.authorEmail,e.createdAt,e.updatedAt,e.resolvedAt??"",e.resolvedBy??""]}function U(e){const[n,...t]=e;return n?t.map(o=>O(n,o)):[]}function O(e,n){return Object.fromEntries(e.map((t,o)=>[t,n[o]??""]))}function x(e){return e.id?{id:e.id,projectKey:e.projectKey,contentId:e.contentId,normalizedPath:e.normalizedPath,originalUrl:e.originalUrl,selector:e.selector,selectorStrategy:e.selectorStrategy==="stable-attribute"?"stable-attribute":"css-path",elementFingerprint:z(e.elementFingerprintJson,{tagName:"",width:0,height:0}),cssSnapshot:z(e.cssSnapshotJson,{margin:"",padding:"",border:"",fontFamily:"",fontSize:"",lineHeight:"",color:"",backgroundColor:"",width:0,height:0}),comment:e.comment,status:e.status==="resolved"?"resolved":"open",authorEmail:e.authorEmail,createdAt:e.createdAt,updatedAt:e.updatedAt,resolvedAt:e.resolvedAt||void 0,resolvedBy:e.resolvedBy||void 0}:null}function z(e,n){try{return e?JSON.parse(e):n}catch{return n}}function _(e){return e==="admin"?["create","read","resolve"]:e==="developer"?["read","resolve"]:["create","read"]}async function X(e){return await Y(),new Promise((n,t)=>{var s;const o=(s=window.google)==null?void 0:s.accounts.oauth2.initTokenClient({client_id:e,scope:J,callback:a=>{if(a.error||!a.access_token){t(new Error(a.error??"Google OAuth did not return an access token"));return}n(a.access_token)}});o==null||o.requestAccessToken({prompt:""})})}function Y(){var e;return(e=window.google)!=null&&e.accounts.oauth2?Promise.resolve():new Promise((n,t)=>{const o=document.querySelector('script[src="https://accounts.google.com/gsi/client"]');if(o){o.addEventListener("load",()=>n(),{once:!0}),o.addEventListener("error",()=>t(new Error("Google Identity failed to load")),{once:!0});return}const s=document.createElement("script");s.src="https://accounts.google.com/gsi/client",s.async=!0,s.defer=!0,s.onload=()=>n(),s.onerror=()=>t(new Error("Google Identity failed to load")),document.head.append(s)})}function B(e){return new URL(e,window.location.href).pathname.replace(/\/+$/,"")||"/"}const K=p.createContext(null);function Q({config:e,children:n}){const t=p.useMemo(()=>e.adapter?e.adapter:T({googleClientId:q(e.googleClientId,"googleClientId"),spreadsheetId:q(e.spreadsheetId,"spreadsheetId"),feedbackSheetName:e.sheetName??"Feedback"}),[e.adapter,e.googleClientId,e.sheetName,e.spreadsheetId]),o=e.currentUrl??window.location.href,s=(e.normalizeUrl??B)(o),[a,y]=p.useState(),[S,c]=p.useState([]),[l,i]=p.useState([]),d=p.useCallback(async()=>{const f=await t.listFeedback({projectKey:e.projectKey,contentId:e.contentId,normalizedPath:s});i(f)},[t,e.contentId,e.projectKey,s]);p.useEffect(()=>{let f=!0;async function v(){const[m,b]=await Promise.all([t.getCurrentUser(),t.getPermissions(e.projectKey)]);f&&(y(m),c(b),await d())}return v(),()=>{f=!1}},[t,e.projectKey,d]);const k=p.useCallback(async f=>{const v=await t.createFeedback(f);return i(m=>[v,...m]),v},[t]),u=p.useCallback(async f=>{const v=await t.resolveFeedback(f,(a==null?void 0:a.email)??"");return i(m=>m.map(b=>b.id===f?v:b)),v},[t,a==null?void 0:a.email]),g=p.useMemo(()=>({config:e,adapter:t,currentUser:a,permissions:S,feedback:l,normalizedPath:s,refreshFeedback:d,createFeedback:k,resolveFeedback:u}),[t,e,k,a,l,s,S,d,u]);return r.jsx(K.Provider,{value:g,children:n})}function M(){const e=p.useContext(K);if(!e)throw new Error("useReviewLens must be used inside ReviewLensProvider");return e}function q(e,n){if(!e)throw new Error(`review-lens-react requires config.${n} when no adapter is provided`);return e}const V=["data-review-id","data-testid","data-test-id","aria-label","name"];function L(e){const n=e.getBoundingClientRect(),t=Z(e);return{selector:t.selector,selectorStrategy:t.strategy,fingerprint:ee(e,n),cssSnapshot:te(e,n),rect:n}}function Z(e){for(const n of V){const t=e.getAttribute(n);if(t)return{selector:`[${n}="${G(t)}"]`,strategy:"stable-attribute"}}return e.id?{selector:`#${G(e.id)}`,strategy:"stable-attribute"}:{selector:R(e),strategy:"css-path"}}function R(e){const n=[];let t=e;for(;t&&t.nodeType===Node.ELEMENT_NODE&&t!==document.body;){const o=t.parentElement,s=t.tagName.toLowerCase();if(!o){n.unshift(s);break}const a=t.tagName,y=Array.from(o.children).filter(c=>c.tagName===a),S=y.indexOf(t)+1;n.unshift(y.length>1?`${s}:nth-of-type(${S})`:s),t=o}return n.join(" > ")}function ee(e,n){var t;return{tagName:e.tagName.toLowerCase(),id:e.id||void 0,className:e.getAttribute("class")||void 0,textSnippet:((t=e.textContent)==null?void 0:t.trim().slice(0,80))||void 0,ariaLabel:e.getAttribute("aria-label")||void 0,width:Math.round(n.width),height:Math.round(n.height)}}function te(e,n){const t=window.getComputedStyle(e);return{margin:j(t.marginTop,t.marginRight,t.marginBottom,t.marginLeft),padding:j(t.paddingTop,t.paddingRight,t.paddingBottom,t.paddingLeft),border:j(t.borderTopWidth,t.borderRightWidth,t.borderBottomWidth,t.borderLeftWidth),fontFamily:t.fontFamily,fontSize:t.fontSize,lineHeight:t.lineHeight,color:t.color,backgroundColor:t.backgroundColor,width:Math.round(n.width),height:Math.round(n.height)}}function j(e,n,t,o){return e===n&&n===t&&t===o?e:`${e} ${n} ${t} ${o}`}function G(e){return typeof CSS<"u"&&typeof CSS.escape=="function"?CSS.escape(e):e.replace(/["\\]/g,"\\$&")}function ne({open:e,onOpenChange:n,placement:t="top-right",showResolved:o=!1}){const{config:s,currentUser:a,feedback:y,normalizedPath:S,permissions:c,createFeedback:l,resolveFeedback:i}=M(),[d,k]=p.useState(),[u,g]=p.useState(),[f,v]=p.useState(""),[m,b]=p.useState(),E=c.includes("create"),P=c.includes("resolve"),A=p.useMemo(()=>y.filter(h=>o||h.status!=="resolved"),[y,o]);p.useEffect(()=>{e||(k(void 0),g(void 0),v(""))},[e]);const N=p.useCallback(h=>{const I=h.target instanceof Element?h.target:null;if(I)return I.closest("[data-review-lens-ui]")?null:I;const C=document.elementFromPoint(h.clientX,h.clientY);return!C||C.closest("[data-review-lens-ui]")?null:C},[]);if(p.useEffect(()=>{if(!e||u)return;function h(C){const F=N(C);F&&k(L(F))}function I(C){const F=N(C);F&&(C.preventDefault(),C.stopPropagation(),g(L(F)))}return window.addEventListener("mousemove",h,!0),window.addEventListener("click",I,!0),()=>{window.removeEventListener("mousemove",h,!0),window.removeEventListener("click",I,!0)}},[N,u,e]),!e)return null;const $=u??d;async function ie(){!u||!f.trim()||!a||!E||(await l({projectKey:s.projectKey,contentId:s.contentId,normalizedPath:S,originalUrl:s.currentUrl??window.location.href,selector:u.selector,selectorStrategy:u.selectorStrategy,elementFingerprint:u.fingerprint,cssSnapshot:u.cssSnapshot,comment:f.trim(),authorEmail:a.email}),v(""),g(void 0))}return r.jsxs("div",{className:"review-lens-root","data-review-lens-ui":!0,children:[$?r.jsx(re,{target:$,locked:!!u}):null,r.jsx(oe,{feedback:A,selectedFeedback:m,onSelect:b}),r.jsxs("aside",{className:`review-lens-panel review-lens-panel--${t}`,"data-review-lens-ui":!0,children:[r.jsxs("header",{className:"review-lens-panel__header",children:[r.jsxs("div",{children:[r.jsx("p",{className:"review-lens-kicker",children:"Review Lens"}),r.jsx("h2",{children:u?"Element locked":"Inspecting"})]}),r.jsx("button",{type:"button",onClick:()=>n==null?void 0:n(!1),children:"Close"})]}),$?r.jsx(se,{target:$}):r.jsx("p",{children:"Move over the app to inspect."}),u?r.jsxs("form",{className:"review-lens-feedback-form",onSubmit:h=>{h.preventDefault(),ie()},children:[r.jsx("label",{htmlFor:"review-lens-comment",children:"Feedback"}),r.jsx("textarea",{id:"review-lens-comment",value:f,disabled:!E,onChange:h=>v(h.target.value),placeholder:E?"Describe the UX issue...":"You do not have permission to comment."}),r.jsxs("div",{className:"review-lens-actions",children:[r.jsx("button",{type:"button",onClick:()=>g(void 0),children:"Unlock"}),r.jsx("button",{type:"submit",disabled:!f.trim()||!E,children:"Save feedback"})]})]}):null,r.jsxs("section",{className:"review-lens-comments",children:[r.jsx("h3",{children:"Page feedback"}),A.length===0?r.jsx("p",{children:"No feedback for this view."}):null,A.map(h=>r.jsxs("article",{className:(m==null?void 0:m.id)===h.id?"review-lens-comment review-lens-comment--selected":"review-lens-comment",children:[r.jsx("p",{children:h.comment}),r.jsx("span",{children:h.authorEmail}),h.status==="open"&&P?r.jsx("button",{type:"button",onClick:()=>void i(h.id),children:"Resolve"}):null]},h.id))]})]})]})}function re({target:e,locked:n}){return r.jsx("div",{className:n?"review-lens-highlight review-lens-highlight--locked":"review-lens-highlight",style:{top:e.rect.top+window.scrollY,left:e.rect.left+window.scrollX,width:e.rect.width,height:e.rect.height}})}function oe({feedback:e,selectedFeedback:n,onSelect:t}){return r.jsx(r.Fragment,{children:e.map(o=>{const s=ae(o.selector),a=s==null?void 0:s.getBoundingClientRect();return a?r.jsx("button",{type:"button",className:(n==null?void 0:n.id)===o.id?"review-lens-marker review-lens-marker--selected":"review-lens-marker",style:{top:a.top+window.scrollY,left:a.left+window.scrollX+a.width},onClick:()=>t(o),"aria-label":`Open feedback from ${o.authorEmail}`},o.id):null})})}function se({target:e}){const n=[["Selector",e.selector],["Size",`${e.cssSnapshot.width} x ${e.cssSnapshot.height}`],["Margin",e.cssSnapshot.margin],["Padding",e.cssSnapshot.padding],["Border",e.cssSnapshot.border],["Font",`${e.cssSnapshot.fontSize} / ${e.cssSnapshot.lineHeight}`],["Family",e.cssSnapshot.fontFamily],["Color",e.cssSnapshot.color],["Background",e.cssSnapshot.backgroundColor]];return r.jsx("dl",{className:"review-lens-metrics",children:n.map(([t,o])=>r.jsxs("div",{children:[r.jsx("dt",{children:t}),r.jsx("dd",{children:o})]},t))})}function ae(e){try{return document.querySelector(e)}catch{return null}}w.ReviewLensOverlay=ne,w.ReviewLensProvider=Q,w.buildElementTarget=L,w.createGoogleSheetsAdapter=T,w.normalizeReviewUrl=B,w.useReviewLens=M,Object.defineProperty(w,Symbol.toStringTag,{value:"Module"})}));
|
|
1
|
+
(function(w,n){typeof exports=="object"&&typeof module<"u"?n(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],n):(w=typeof globalThis<"u"?globalThis:w||self,n(w.ReviewLensReact={},w.jsxRuntime,w.React))})(this,(function(w,n,p){"use strict";const G=["https://www.googleapis.com/auth/spreadsheets","https://www.googleapis.com/auth/userinfo.email"].join(" "),J="https://www.googleapis.com/oauth2/v3/userinfo";function $(e){const o=e.feedbackSheetName??"Feedback",t=e.usersSheetName??"Users";let r,s;async function i(){return r??(r=X(e.googleClientId)),r}async function m(d,c){const a=await i(),l=await fetch(`https://sheets.googleapis.com/v4/spreadsheets/${e.spreadsheetId}${d}`,{...c,headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/json",...c==null?void 0:c.headers}});if(!l.ok)throw new Error(`Google Sheets request failed with ${l.status}`);return l.json()}async function k(d){return(await m(`/values/${encodeURIComponent(d)}`)).values??[]}return{async getCurrentUser(){if(!s){const d=await i(),c=await fetch(J,{headers:{Authorization:`Bearer ${d}`}});if(!c.ok)throw new Error(`Google userinfo request failed with ${c.status}`);s=(await c.json()).email}if(!s)throw new Error("Google account did not return an email address");return{email:s}},async getPermissions(d){const[{email:c},a]=await Promise.all([this.getCurrentUser(),k(t)]),l=j(a),C=c.toLowerCase(),h=l.find(b=>{var g;return((g=b.email)==null?void 0:g.toLowerCase())===C&&b.active!=="false"&&(!b.projectKey||b.projectKey===d)});return Q((h==null?void 0:h.role)??"designer")},async listFeedback(d){return j(await k(o)).map(M).filter(a=>a!==null).filter(a=>a.projectKey===d.projectKey&&a.contentId===d.contentId&&a.normalizedPath===d.normalizedPath).sort((a,l)=>l.createdAt.localeCompare(a.createdAt))},async createFeedback(d){const c=new Date().toISOString(),a={...d,id:crypto.randomUUID(),status:"open",createdAt:c,updatedAt:c};return await m(`/values/${encodeURIComponent(o)}:append?valueInputOption=RAW`,{method:"POST",body:JSON.stringify({values:[H(a)]})}),a},async resolveFeedback(d,c){const a=await k(o),l=a[0]??R,C=l.indexOf("id"),h=l.indexOf("status"),b=l.indexOf("updatedAt"),g=l.indexOf("resolvedAt"),v=l.indexOf("resolvedBy"),f=a.findIndex((T,B)=>B>0&&T[C]===d);if(f<1)throw new Error(`Feedback ${d} was not found`);const y=[...a[f]],E=new Date().toISOString();y[h]="resolved",y[b]=E,y[g]=E,y[v]=c,await m(`/values/${encodeURIComponent(o)}!A${f+1}:Q${f+1}?valueInputOption=RAW`,{method:"PUT",body:JSON.stringify({values:[y]})});const F=M(W(l,y));if(!F)throw new Error(`Feedback ${d} could not be parsed after resolving`);return F}}}const R=["id","projectKey","contentId","normalizedPath","originalUrl","selector","selectorStrategy","elementFingerprintJson","cssSnapshotJson","comment","status","authorEmail","createdAt","updatedAt","resolvedAt","resolvedBy"];function H(e){return[e.id,e.projectKey,e.contentId,e.normalizedPath,e.originalUrl,e.selector,e.selectorStrategy,JSON.stringify(e.elementFingerprint),JSON.stringify(e.cssSnapshot),e.comment,e.status,e.authorEmail,e.createdAt,e.updatedAt,e.resolvedAt??"",e.resolvedBy??""]}function j(e){const[o,...t]=e;return o?t.map(r=>W(o,r)):[]}function W(e,o){return Object.fromEntries(e.map((t,r)=>[t,o[r]??""]))}function M(e){return e.id?{id:e.id,projectKey:e.projectKey,contentId:e.contentId,normalizedPath:e.normalizedPath,originalUrl:e.originalUrl,selector:e.selector,selectorStrategy:e.selectorStrategy==="stable-attribute"?"stable-attribute":"css-path",elementFingerprint:U(e.elementFingerprintJson,{tagName:"",width:0,height:0}),cssSnapshot:D(e.cssSnapshotJson),comment:e.comment,status:e.status==="resolved"?"resolved":"open",authorEmail:e.authorEmail,createdAt:e.createdAt,updatedAt:e.updatedAt,resolvedAt:e.resolvedAt||void 0,resolvedBy:e.resolvedBy||void 0}:null}function U(e,o){try{return e?JSON.parse(e):o}catch{return o}}function D(e){const o=U(e,{});return{margin:o.margin??"",marginTop:o.marginTop??"",marginRight:o.marginRight??"",marginBottom:o.marginBottom??"",marginLeft:o.marginLeft??"",padding:o.padding??"",paddingTop:o.paddingTop??"",paddingRight:o.paddingRight??"",paddingBottom:o.paddingBottom??"",paddingLeft:o.paddingLeft??"",border:o.border??"",borderTopWidth:o.borderTopWidth??"",borderRightWidth:o.borderRightWidth??"",borderBottomWidth:o.borderBottomWidth??"",borderLeftWidth:o.borderLeftWidth??"",fontFamily:o.fontFamily??"",fontSize:o.fontSize??"",lineHeight:o.lineHeight??"",color:o.color??"",backgroundColor:o.backgroundColor??"",width:o.width??0,height:o.height??0}}function Q(e){return e==="admin"?["create","read","resolve"]:e==="developer"?["read","resolve"]:["create","read"]}async function X(e){return await Y(),new Promise((o,t)=>{var s;const r=(s=window.google)==null?void 0:s.accounts.oauth2.initTokenClient({client_id:e,scope:G,callback:i=>{if(i.error||!i.access_token){t(new Error(i.error??"Google OAuth did not return an access token"));return}o(i.access_token)}});r==null||r.requestAccessToken({prompt:""})})}function Y(){var e;return(e=window.google)!=null&&e.accounts.oauth2?Promise.resolve():new Promise((o,t)=>{const r=document.querySelector('script[src="https://accounts.google.com/gsi/client"]');if(r){r.addEventListener("load",()=>o(),{once:!0}),r.addEventListener("error",()=>t(new Error("Google Identity failed to load")),{once:!0});return}const s=document.createElement("script");s.src="https://accounts.google.com/gsi/client",s.async=!0,s.defer=!0,s.onload=()=>o(),s.onerror=()=>t(new Error("Google Identity failed to load")),document.head.append(s)})}function O(e){return new URL(e,window.location.href).pathname.replace(/\/+$/,"")||"/"}const z=p.createContext(null);function V({config:e,children:o}){const t=p.useMemo(()=>e.adapter?e.adapter:$({googleClientId:_(e.googleClientId,"googleClientId"),spreadsheetId:_(e.spreadsheetId,"spreadsheetId"),feedbackSheetName:e.sheetName??"Feedback"}),[e.adapter,e.googleClientId,e.sheetName,e.spreadsheetId]),r=e.currentUrl??window.location.href,s=(e.normalizeUrl??O)(r),[i,m]=p.useState(),[k,d]=p.useState([]),[c,a]=p.useState([]),l=p.useCallback(async()=>{const g=await t.listFeedback({projectKey:e.projectKey,contentId:e.contentId,normalizedPath:s});a(g)},[t,e.contentId,e.projectKey,s]);p.useEffect(()=>{let g=!0;async function v(){const[f,y]=await Promise.all([t.getCurrentUser(),t.getPermissions(e.projectKey)]);g&&(m(f),d(y),await l())}return v(),()=>{g=!1}},[t,e.projectKey,l]);const C=p.useCallback(async g=>{const v=await t.createFeedback(g);return a(f=>[v,...f]),v},[t]),h=p.useCallback(async g=>{const v=await t.resolveFeedback(g,(i==null?void 0:i.email)??"");return a(f=>f.map(y=>y.id===g?v:y)),v},[t,i==null?void 0:i.email]),b=p.useMemo(()=>({config:e,adapter:t,currentUser:i,permissions:k,feedback:c,normalizedPath:s,refreshFeedback:l,createFeedback:C,resolveFeedback:h}),[t,e,C,i,c,s,k,l,h]);return n.jsx(z.Provider,{value:b,children:o})}function K(){const e=p.useContext(z);if(!e)throw new Error("useReviewLens must be used inside ReviewLensProvider");return e}function _(e,o){if(!e)throw new Error(`review-lens-react requires config.${o} when no adapter is provided`);return e}const Z=["data-review-id","data-testid","data-test-id","aria-label","name"];function A(e){const o=e.getBoundingClientRect(),t=ee(e);return{selector:t.selector,selectorStrategy:t.strategy,fingerprint:oe(e,o),cssSnapshot:ne(e,o),rect:o}}function ee(e){for(const o of Z){const t=e.getAttribute(o);if(t)return{selector:`[${o}="${q(t)}"]`,strategy:"stable-attribute"}}return e.id?{selector:`#${q(e.id)}`,strategy:"stable-attribute"}:{selector:te(e),strategy:"css-path"}}function te(e){const o=[];let t=e;for(;t&&t.nodeType===Node.ELEMENT_NODE&&t!==document.body;){const r=t.parentElement,s=t.tagName.toLowerCase();if(!r){o.unshift(s);break}const i=t.tagName,m=Array.from(r.children).filter(d=>d.tagName===i),k=m.indexOf(t)+1;o.unshift(m.length>1?`${s}:nth-of-type(${k})`:s),t=r}return o.join(" > ")}function oe(e,o){var t;return{tagName:e.tagName.toLowerCase(),id:e.id||void 0,className:e.getAttribute("class")||void 0,textSnippet:((t=e.textContent)==null?void 0:t.trim().slice(0,80))||void 0,ariaLabel:e.getAttribute("aria-label")||void 0,width:Math.round(o.width),height:Math.round(o.height)}}function ne(e,o){const t=window.getComputedStyle(e);return{margin:P(t.marginTop,t.marginRight,t.marginBottom,t.marginLeft),marginTop:t.marginTop,marginRight:t.marginRight,marginBottom:t.marginBottom,marginLeft:t.marginLeft,padding:P(t.paddingTop,t.paddingRight,t.paddingBottom,t.paddingLeft),paddingTop:t.paddingTop,paddingRight:t.paddingRight,paddingBottom:t.paddingBottom,paddingLeft:t.paddingLeft,border:P(t.borderTopWidth,t.borderRightWidth,t.borderBottomWidth,t.borderLeftWidth),borderTopWidth:t.borderTopWidth,borderRightWidth:t.borderRightWidth,borderBottomWidth:t.borderBottomWidth,borderLeftWidth:t.borderLeftWidth,fontFamily:t.fontFamily,fontSize:t.fontSize,lineHeight:t.lineHeight,color:t.color,backgroundColor:t.backgroundColor,width:Math.round(o.width),height:Math.round(o.height)}}function P(e,o,t,r){return e===o&&o===t&&t===r?e:`${e} ${o} ${t} ${r}`}function q(e){return typeof CSS<"u"&&typeof CSS.escape=="function"?CSS.escape(e):e.replace(/["\\]/g,"\\$&")}function re({open:e,onOpenChange:o,placement:t="top-right",showResolved:r=!1}){const{config:s,currentUser:i,feedback:m,normalizedPath:k,permissions:d,createFeedback:c,resolveFeedback:a}=K(),[l,C]=p.useState(),[h,b]=p.useState(),[g,v]=p.useState(""),[f,y]=p.useState(),E=d.includes("create"),F=d.includes("resolve"),T=p.useMemo(()=>m.filter(u=>r||u.status!=="resolved"),[m,r]);p.useEffect(()=>{e||(C(void 0),b(void 0),v(""))},[e]);const B=p.useCallback(u=>{const N=u.target instanceof Element?u.target:null;if(N)return N.closest("[data-review-lens-ui]")?null:N;const L=document.elementFromPoint(u.clientX,u.clientY);return!L||L.closest("[data-review-lens-ui]")?null:L},[]);if(p.useEffect(()=>{if(!e||h)return;function u(L){const I=B(L);C(I?A(I):void 0)}function N(L){const I=B(L);I&&(L.preventDefault(),L.stopPropagation(),b(A(I)))}return window.addEventListener("mousemove",u,!0),window.addEventListener("click",N,!0),()=>{window.removeEventListener("mousemove",u,!0),window.removeEventListener("click",N,!0)}},[B,h,e]),!e)return null;const x=h??l;async function le(){!h||!g.trim()||!i||!E||(await c({projectKey:s.projectKey,contentId:s.contentId,normalizedPath:k,originalUrl:s.currentUrl??window.location.href,selector:h.selector,selectorStrategy:h.selectorStrategy,elementFingerprint:h.fingerprint,cssSnapshot:h.cssSnapshot,comment:g.trim(),authorEmail:i.email}),v(""),b(void 0))}return n.jsxs("div",{className:"review-lens-root","data-review-lens-ui":!0,children:[x?n.jsx(se,{target:x,locked:!!h}):null,n.jsx(ie,{feedback:T,selectedFeedback:f,onSelect:y}),n.jsxs("aside",{className:`review-lens-panel review-lens-panel--${t}`,"data-review-lens-ui":!0,children:[n.jsxs("header",{className:"review-lens-panel__header",children:[n.jsxs("div",{children:[n.jsx("p",{className:"review-lens-kicker",children:"Review Lens"}),n.jsx("h2",{children:h?"Element locked":"Inspecting"})]}),n.jsx("button",{type:"button",onClick:()=>o==null?void 0:o(!1),children:"Close"})]}),x?n.jsx(ae,{target:x}):n.jsx("p",{children:"Move over the app to inspect."}),h?n.jsxs("form",{className:"review-lens-feedback-form",onSubmit:u=>{u.preventDefault(),le()},children:[n.jsx("label",{htmlFor:"review-lens-comment",children:"Feedback"}),n.jsx("textarea",{id:"review-lens-comment",value:g,disabled:!E,onChange:u=>v(u.target.value),placeholder:E?"Describe the UX issue...":"You do not have permission to comment."}),n.jsxs("div",{className:"review-lens-actions",children:[n.jsx("button",{type:"button",onClick:()=>b(void 0),children:"Unlock"}),n.jsx("button",{type:"submit",disabled:!g.trim()||!E,children:"Save feedback"})]})]}):null,n.jsxs("section",{className:"review-lens-comments",children:[n.jsx("h3",{children:"Page feedback"}),T.length===0?n.jsx("p",{children:"No feedback for this view."}):null,T.map(u=>n.jsxs("article",{className:(f==null?void 0:f.id)===u.id?"review-lens-comment review-lens-comment--selected":"review-lens-comment",children:[n.jsx("p",{children:u.comment}),n.jsx("span",{children:u.authorEmail}),u.status==="open"&&F?n.jsx("button",{type:"button",onClick:()=>void a(u.id),children:"Resolve"}):null]},u.id))]})]})]})}function se({target:e,locked:o}){const t=ce(e);return n.jsxs("div",{className:o?"review-lens-highlight review-lens-highlight--locked":"review-lens-highlight",style:{top:t.margin.top,left:t.margin.left,width:t.margin.width,height:t.margin.height},children:[n.jsx("div",{className:"review-lens-highlight__border",style:{top:t.border.top-t.margin.top,left:t.border.left-t.margin.left,width:t.border.width,height:t.border.height}}),n.jsx("div",{className:"review-lens-highlight__padding",style:{top:t.padding.top-t.margin.top,left:t.padding.left-t.margin.left,width:t.padding.width,height:t.padding.height}}),n.jsx("div",{className:"review-lens-highlight__content",style:{top:t.content.top-t.margin.top,left:t.content.left-t.margin.left,width:t.content.width,height:t.content.height}}),n.jsxs("div",{className:"review-lens-highlight__label",children:[Math.round(e.rect.width)," x ",Math.round(e.rect.height)]})]})}function ie({feedback:e,selectedFeedback:o,onSelect:t}){return n.jsx(n.Fragment,{children:e.map(r=>{const s=de(r.selector),i=s==null?void 0:s.getBoundingClientRect();return i?n.jsx("button",{type:"button",className:(o==null?void 0:o.id)===r.id?"review-lens-marker review-lens-marker--selected":"review-lens-marker",style:{top:i.top,left:i.left+i.width},onClick:()=>t(r),"aria-label":`Open feedback from ${r.authorEmail}`},r.id):null})})}function ae({target:e}){const o=[["Selector",e.selector],["Size",`${e.cssSnapshot.width} x ${e.cssSnapshot.height}`],["Margin",e.cssSnapshot.margin],["Padding",e.cssSnapshot.padding],["Border",e.cssSnapshot.border],["Font",`${e.cssSnapshot.fontSize} / ${e.cssSnapshot.lineHeight}`],["Family",e.cssSnapshot.fontFamily],["Color",e.cssSnapshot.color],["Background",e.cssSnapshot.backgroundColor]];return n.jsx("dl",{className:"review-lens-metrics",children:o.map(([t,r])=>n.jsxs("div",{children:[n.jsx("dt",{children:t}),n.jsx("dd",{children:r})]},t))})}function de(e){try{return document.querySelector(e)}catch{return null}}function ce(e){const o={top:S(e.cssSnapshot.marginTop),right:S(e.cssSnapshot.marginRight),bottom:S(e.cssSnapshot.marginBottom),left:S(e.cssSnapshot.marginLeft)},t={top:S(e.cssSnapshot.borderTopWidth),right:S(e.cssSnapshot.borderRightWidth),bottom:S(e.cssSnapshot.borderBottomWidth),left:S(e.cssSnapshot.borderLeftWidth)},r={top:S(e.cssSnapshot.paddingTop),right:S(e.cssSnapshot.paddingRight),bottom:S(e.cssSnapshot.paddingBottom),left:S(e.cssSnapshot.paddingLeft)},s={top:e.rect.top,left:e.rect.left,width:Math.max(e.rect.width,0),height:Math.max(e.rect.height,0)},i={top:s.top-o.top,left:s.left-o.left,width:s.width+o.left+o.right,height:s.height+o.top+o.bottom},m={top:s.top+t.top,left:s.left+t.left,width:Math.max(s.width-t.left-t.right,0),height:Math.max(s.height-t.top-t.bottom,0)},k={top:m.top+r.top,left:m.left+r.left,width:Math.max(m.width-r.left-r.right,0),height:Math.max(m.height-r.top-r.bottom,0)};return{margin:i,border:s,padding:m,content:k}}function S(e){const o=Number.parseFloat(e||"0");return Number.isFinite(o)?o:0}w.ReviewLensOverlay=re,w.ReviewLensProvider=V,w.buildElementTarget=A,w.createGoogleSheetsAdapter=$,w.normalizeReviewUrl=O,w.useReviewLens=K,Object.defineProperty(w,Symbol.toStringTag,{value:"Module"})}));
|
package/dist/styles.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.review-lens-root{color:#171717;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;position:fixed;z-index:2147483647}.review-lens-highlight{
|
|
1
|
+
.review-lens-root{color:#171717;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;top:0;right:0;bottom:0;left:0;pointer-events:none;position:fixed;z-index:2147483647}.review-lens-highlight{background:#f9731633;box-shadow:0 0 0 9999px #0f172a14;box-sizing:border-box;pointer-events:none;position:fixed}.review-lens-highlight--locked{box-shadow:0 0 0 9999px #0f172a1f}.review-lens-highlight__border,.review-lens-highlight__padding,.review-lens-highlight__content{box-sizing:border-box;position:absolute}.review-lens-highlight__border{border:2px solid #facc15;background:#facc1538}.review-lens-highlight__padding{border:2px solid #22c55e;background:#22c55e38}.review-lens-highlight__content{border:2px solid #2563eb;background:#2563eb29}.review-lens-highlight--locked .review-lens-highlight__content{border-color:#f97316}.review-lens-highlight__label{background:#171717;border-radius:4px;color:#fff;font-size:11px;font-weight:700;left:0;line-height:1;padding:4px 6px;position:absolute;top:-24px;white-space:nowrap}.review-lens-panel{background:#fafafa;border:1px solid #d4d4d4;border-radius:8px;box-shadow:0 24px 70px #0f172a38;box-sizing:border-box;max-height:calc(100vh - 32px);overflow:auto;padding:16px;pointer-events:auto;position:fixed;width:min(380px,calc(100vw - 32px))}.review-lens-panel--top-left{left:16px;top:16px}.review-lens-panel--top-right{right:16px;top:16px}.review-lens-panel--bottom-left{bottom:16px;left:16px}.review-lens-panel--bottom-right{bottom:16px;right:16px}.review-lens-panel__header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}.review-lens-panel h2,.review-lens-panel h3,.review-lens-panel p{margin:0}.review-lens-panel h2{font-size:18px;line-height:1.25}.review-lens-panel h3{font-size:14px;margin-top:18px}.review-lens-kicker{color:#525252;font-size:11px;font-weight:700;letter-spacing:0;text-transform:uppercase}.review-lens-panel button{background:#171717;border:1px solid #171717;border-radius:6px;color:#fff;cursor:pointer;font:inherit;font-size:13px;min-height:32px;padding:6px 10px}.review-lens-panel button:disabled{cursor:not-allowed;opacity:.45}.review-lens-metrics{border:1px solid #e5e5e5;border-radius:8px;display:grid;gap:0;margin:16px 0 0;overflow:hidden}.review-lens-metrics div{display:grid;grid-template-columns:96px minmax(0,1fr)}.review-lens-metrics dt,.review-lens-metrics dd{border-bottom:1px solid #e5e5e5;font-size:12px;margin:0;min-width:0;padding:8px}.review-lens-metrics dt{background:#f5f5f5;color:#525252;font-weight:700}.review-lens-metrics dd{overflow-wrap:anywhere}.review-lens-feedback-form{display:grid;gap:8px;margin-top:16px}.review-lens-feedback-form label{font-size:13px;font-weight:700}.review-lens-feedback-form textarea{border:1px solid #d4d4d4;border-radius:8px;box-sizing:border-box;font:inherit;min-height:96px;padding:10px;resize:vertical;width:100%}.review-lens-actions{display:flex;gap:8px;justify-content:flex-end}.review-lens-comments{display:grid;gap:8px}.review-lens-comment{border:1px solid #e5e5e5;border-radius:8px;display:grid;gap:6px;padding:10px}.review-lens-comment--selected{border-color:#2563eb}.review-lens-comment span{color:#525252;font-size:12px}.review-lens-marker{background:#f97316;border:2px solid #ffffff;border-radius:999px;box-shadow:0 8px 20px #0f172a3d;cursor:pointer;height:18px;pointer-events:auto;position:fixed;transform:translate(-50%,-50%);width:18px}.review-lens-marker--selected{background:#2563eb}
|
package/dist/types.d.ts
CHANGED
|
@@ -4,8 +4,20 @@ export type ReviewLensRole = "designer" | "developer" | "admin";
|
|
|
4
4
|
export type ReviewLensPermission = "create" | "read" | "resolve";
|
|
5
5
|
export type CssSnapshot = {
|
|
6
6
|
margin: string;
|
|
7
|
+
marginTop: string;
|
|
8
|
+
marginRight: string;
|
|
9
|
+
marginBottom: string;
|
|
10
|
+
marginLeft: string;
|
|
7
11
|
padding: string;
|
|
12
|
+
paddingTop: string;
|
|
13
|
+
paddingRight: string;
|
|
14
|
+
paddingBottom: string;
|
|
15
|
+
paddingLeft: string;
|
|
8
16
|
border: string;
|
|
17
|
+
borderTopWidth: string;
|
|
18
|
+
borderRightWidth: string;
|
|
19
|
+
borderBottomWidth: string;
|
|
20
|
+
borderLeftWidth: string;
|
|
9
21
|
fontFamily: string;
|
|
10
22
|
fontSize: string;
|
|
11
23
|
lineHeight: string;
|