@mahmulp/feedback-sdk 0.0.3 → 0.0.5

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/index.cjs CHANGED
@@ -318,11 +318,34 @@ var PopoverManager = class {
318
318
  });
319
319
  return el;
320
320
  }
321
+ /** True when a thread for this feedback is already open. */
322
+ isThreadOpenFor(feedbackId) {
323
+ return this.current?.type === "thread" && this.current.feedbackId === feedbackId;
324
+ }
321
325
  showThread(feedback, anchor, cb) {
326
+ if (this.current?.type === "thread" && this.current.feedbackId === feedback.id) {
327
+ const fresh = this.buildThread(feedback, cb);
328
+ this.current.el.replaceWith(fresh);
329
+ this.current = {
330
+ type: "thread",
331
+ el: fresh,
332
+ pageX: anchor.pageX,
333
+ pageY: anchor.pageY,
334
+ feedbackId: feedback.id
335
+ };
336
+ this.repositionInternal();
337
+ return fresh;
338
+ }
322
339
  this.hide();
323
340
  const el = this.buildThread(feedback, cb);
324
341
  this.layer.appendChild(el);
325
- this.current = { type: "thread", el, pageX: anchor.pageX, pageY: anchor.pageY };
342
+ this.current = {
343
+ type: "thread",
344
+ el,
345
+ pageX: anchor.pageX,
346
+ pageY: anchor.pageY,
347
+ feedbackId: feedback.id
348
+ };
326
349
  this.repositionInternal();
327
350
  return el;
328
351
  }
@@ -1030,33 +1053,65 @@ var Overlay = class {
1030
1053
  });
1031
1054
  }
1032
1055
  layoutPins() {
1033
- const next = document.createDocumentFragment();
1056
+ const existing = /* @__PURE__ */ new Map();
1057
+ for (const node of Array.from(this.pinLayer.children)) {
1058
+ const id = node.dataset.feedbackId;
1059
+ if (id) existing.set(id, node);
1060
+ }
1061
+ const desiredOrder = [];
1062
+ const seen = /* @__PURE__ */ new Set();
1034
1063
  for (const fb of this.pendingFeedback) {
1064
+ seen.add(fb.id);
1035
1065
  const target = findElement(fb.selector);
1036
1066
  const projected = projectCoordinates(target, fb.coordinates);
1037
- const pin = document.createElement("button");
1038
- pin.type = "button";
1039
- const classes = ["pin"];
1040
- if (projected.orphaned) classes.push("orphaned");
1041
- if (fb.status === "resolved") classes.push("resolved");
1042
- if (fb.status === "archived") classes.push("archived");
1043
- pin.className = classes.join(" ");
1044
- pin.setAttribute("data-feedback-id", fb.id);
1045
- pin.setAttribute("aria-label", `Feedback ${fb.id}`);
1046
- pin.style.left = `${projected.x - window.scrollX}px`;
1047
- pin.style.top = `${projected.y - window.scrollY}px`;
1048
- const label = document.createElement("span");
1049
- label.textContent = String(fb.thread.length || 1);
1050
- pin.appendChild(label);
1051
- pin.addEventListener("click", (e) => {
1052
- e.stopPropagation();
1053
- if (this.draggingPin === fb.id) return;
1054
- this.onPinClick?.(fb);
1055
- });
1056
- this.attachDragHandlers(pin, fb);
1057
- next.appendChild(pin);
1067
+ const left = `${projected.x - window.scrollX}px`;
1068
+ const top = `${projected.y - window.scrollY}px`;
1069
+ let pin = existing.get(fb.id);
1070
+ if (pin) {
1071
+ const classes = ["pin"];
1072
+ if (projected.orphaned) classes.push("orphaned");
1073
+ if (fb.status === "resolved") classes.push("resolved");
1074
+ if (fb.status === "archived") classes.push("archived");
1075
+ if (pin.classList.contains("dragging")) classes.push("dragging");
1076
+ pin.className = classes.join(" ");
1077
+ if (pin.style.left !== left) pin.style.left = left;
1078
+ if (pin.style.top !== top) pin.style.top = top;
1079
+ const label = pin.firstElementChild;
1080
+ const nextLabel = String(fb.thread.length || 1);
1081
+ if (label && label.textContent !== nextLabel) label.textContent = nextLabel;
1082
+ } else {
1083
+ pin = document.createElement("button");
1084
+ pin.type = "button";
1085
+ const classes = ["pin"];
1086
+ if (projected.orphaned) classes.push("orphaned");
1087
+ if (fb.status === "resolved") classes.push("resolved");
1088
+ if (fb.status === "archived") classes.push("archived");
1089
+ pin.className = classes.join(" ");
1090
+ pin.dataset.feedbackId = fb.id;
1091
+ pin.setAttribute("aria-label", `Feedback ${fb.id}`);
1092
+ pin.style.left = left;
1093
+ pin.style.top = top;
1094
+ const label = document.createElement("span");
1095
+ label.textContent = String(fb.thread.length || 1);
1096
+ pin.appendChild(label);
1097
+ pin.addEventListener("click", (e) => {
1098
+ e.stopPropagation();
1099
+ if (this.draggingPin === fb.id) return;
1100
+ const fresh = this.pendingFeedback.find((f) => f.id === fb.id) ?? fb;
1101
+ this.onPinClick?.(fresh);
1102
+ });
1103
+ this.attachDragHandlers(pin, fb);
1104
+ }
1105
+ desiredOrder.push(pin);
1106
+ }
1107
+ for (const [id, node] of existing) {
1108
+ if (!seen.has(id)) node.remove();
1109
+ }
1110
+ for (let i = 0; i < desiredOrder.length; i++) {
1111
+ const want = desiredOrder[i];
1112
+ const have = this.pinLayer.children[i];
1113
+ if (have !== want) this.pinLayer.insertBefore(want, have ?? null);
1058
1114
  }
1059
- this.pinLayer.replaceChildren(next);
1060
1115
  }
1061
1116
  destroy() {
1062
1117
  if (this.rafHandle !== null) {
@@ -1509,8 +1564,6 @@ function initFeedback(options) {
1509
1564
  const updated = await transport.reply(fb.id, { author, body });
1510
1565
  setAuthor(author);
1511
1566
  replaceFeedback(updated);
1512
- overlay.popoverManager().hide();
1513
- activeThreadId = null;
1514
1567
  openThread(updated);
1515
1568
  } catch (err) {
1516
1569
  reportError(err);
@@ -1520,8 +1573,6 @@ function initFeedback(options) {
1520
1573
  try {
1521
1574
  const updated = await transport.setStatus(fb.id, status);
1522
1575
  replaceFeedback(updated);
1523
- overlay.popoverManager().hide();
1524
- activeThreadId = null;
1525
1576
  openThread(updated);
1526
1577
  } catch (err) {
1527
1578
  reportError(err);
@@ -1569,7 +1620,8 @@ function initFeedback(options) {
1569
1620
  }
1570
1621
  async function refresh() {
1571
1622
  try {
1572
- const result = await transport.list({ projectId: "" });
1623
+ const pageUrl = getPageUrl();
1624
+ const result = await transport.list({ projectId: "", pageUrl });
1573
1625
  state.feedbacks = result.items;
1574
1626
  overlay.renderPins(state.feedbacks, openThread, onPinDragEnd);
1575
1627
  } catch (err) {
@@ -1599,6 +1651,26 @@ function initFeedback(options) {
1599
1651
  document.addEventListener("keydown", onKeyDown, true);
1600
1652
  window.addEventListener("scroll", onWindowReposition, true);
1601
1653
  window.addEventListener("resize", onWindowReposition);
1654
+ let lastSeenUrl = getPageUrl();
1655
+ function onMaybeNavigate() {
1656
+ const next = getPageUrl();
1657
+ if (next === lastSeenUrl) return;
1658
+ lastSeenUrl = next;
1659
+ void refresh();
1660
+ }
1661
+ const originalPushState = history.pushState.bind(history);
1662
+ const originalReplaceState = history.replaceState.bind(history);
1663
+ history.pushState = function patchedPushState(...args) {
1664
+ const result = originalPushState(...args);
1665
+ queueMicrotask(onMaybeNavigate);
1666
+ return result;
1667
+ };
1668
+ history.replaceState = function patchedReplaceState(...args) {
1669
+ const result = originalReplaceState(...args);
1670
+ queueMicrotask(onMaybeNavigate);
1671
+ return result;
1672
+ };
1673
+ window.addEventListener("popstate", onMaybeNavigate);
1602
1674
  void refresh();
1603
1675
  overlay.setEnabledStyles(state.enabled);
1604
1676
  const wantsLauncher = options.showLauncher !== false;
@@ -1661,6 +1733,9 @@ function initFeedback(options) {
1661
1733
  document.removeEventListener("keydown", onKeyDown, true);
1662
1734
  window.removeEventListener("scroll", onWindowReposition, true);
1663
1735
  window.removeEventListener("resize", onWindowReposition);
1736
+ window.removeEventListener("popstate", onMaybeNavigate);
1737
+ history.pushState = originalPushState;
1738
+ history.replaceState = originalReplaceState;
1664
1739
  overlay.destroy();
1665
1740
  }
1666
1741
  };