@stll/folio-react 0.20.1 → 0.20.3

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.
@@ -1,9 +1,10 @@
1
- import { d as containedHandler } from "./renderAsync-B4R5bi9J.js";
1
+ import { d as containedHandler } from "./renderAsync-DMNxIBO9.js";
2
2
  import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
3
3
  import { CheckIcon, MoreVerticalIcon } from "lucide-react";
4
4
  import { useLocale, useTranslations } from "use-intl";
5
5
  import { closestHtmlElement, queryHtmlElement } from "@stll/folio-core/utils/domGuards";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { commentAnchorSelector } from "@stll/folio-core/render-dom/commentAnchorAttributes";
7
8
  //#region src/components/CommentsSidebar.tsx
8
9
  /**
9
10
  * Comments Sidebar
@@ -174,7 +175,7 @@ const CommentsSidebar = ({ comments, onCommentClick, activeCommentId = null, onC
174
175
  };
175
176
  for (const comment of visibleComments) {
176
177
  const cardId = `comment-${comment.id}`;
177
- const el = pagesEl.querySelector(`[data-comment-id="${CSS.escape(String(comment.id))}"]`);
178
+ const el = pagesEl.querySelector(commentAnchorSelector(comment.id));
178
179
  if (el) {
179
180
  pushPosition(cardId, el.getBoundingClientRect().top - containerRect.top + scrollTop, cardRefs.current.get(cardId)?.offsetHeight || DEFAULT_CARD_HEIGHT);
180
181
  continue;
@@ -1,4 +1,4 @@
1
- import { n as DocxEditor$1, t as renderAsync } from "../renderAsync-B4R5bi9J.js";
1
+ import { n as DocxEditor$1, t as renderAsync } from "../renderAsync-DMNxIBO9.js";
2
2
  import { t as messages_exports } from "../messages.js";
3
3
  import { c } from "react-compiler-runtime";
4
4
  import { forwardRef } from "react";
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as formatZoom, c as FormattingBar, i as clampZoom, l as ZoomControl, n as DocxEditor, o as parseZoom, r as ZOOM_PRESETS, s as useWheelZoom, t as renderAsync, u as AutocompleteCaretOverlay } from "./renderAsync-B4R5bi9J.js";
1
+ import { a as formatZoom, c as FormattingBar, i as clampZoom, l as ZoomControl, n as DocxEditor, o as parseZoom, r as ZOOM_PRESETS, s as useWheelZoom, t as renderAsync, u as AutocompleteCaretOverlay } from "./renderAsync-DMNxIBO9.js";
2
2
  import { n as FolioUIProvider } from "./folio-ui-BgDDRsUX.js";
3
3
  import { t as FindReplaceDialog } from "./FindReplaceDialog-CqKK82nE.js";
4
4
  import { t as FootnotePropertiesDialog } from "./FootnotePropertiesDialog-CixRdIiT.js";
@@ -103,11 +103,12 @@ import { DISPLAY_MODES, EditorModeManager, isReadOnlyMode, isTrackChangesMode, t
103
103
  import { FindReplaceManager } from "@stll/folio-core/managers/FindReplaceManager";
104
104
  import { createEmptyHeaderFooter, pickActiveHeaderFooterRId, removeHeaderFooter, resolveEffectiveSectionProperties, resolveHeaderFooterContent, saveHeaderFooterContent } from "@stll/folio-core/utils/headerFooter";
105
105
  import { editHyperlinkAtCursor, removeHyperlinkAtCursor } from "@stll/folio-core/prosemirror/commands/hyperlink";
106
- import { sanitizeExternalUrl } from "@stll/folio-core/utils/urlSecurity";
106
+ import { anchorTargetAttrs, sanitizeExternalUrl, windowFeaturesFor } from "@stll/folio-core/utils/urlSecurity";
107
107
  import { applyImagePosition, applyImageProperties, applyImageTransform, applyImageWrapType, insertImageFromFile } from "@stll/folio-core/prosemirror/commands/image";
108
108
  import { classifyEditorKeydown, deleteSelectedTable, isFocusInInputLike, isKeydownInShortcutScope, isMacPlatform } from "@stll/folio-core/managers/editorShortcuts";
109
109
  import { getScrollTopForZoomAnchor, getViewportCenterZoomAnchorForZoomChange } from "@stll/folio-core/paged-layout/zoomScrollAnchor";
110
110
  import { ZOOM_MAX, ZOOM_MIN, ZOOM_STEP } from "@stll/folio-core/utils/zoom";
111
+ import { COMMENT_ANCHOR_SELECTOR, commentAnchorIds } from "@stll/folio-core/render-dom/commentAnchorAttributes";
111
112
  import { isValidHexId } from "@stll/folio-core/utils/hexId";
112
113
  import { createRoot } from "react-dom/client";
113
114
  //#region src/hooks/useHistory.ts
@@ -210,7 +211,7 @@ function useDocumentHistory(document, t0) {
210
211
  $[1] = t1;
211
212
  } else t1 = $[1];
212
213
  const options = t1;
213
- const isEqual = _temp$11;
214
+ const isEqual = _temp$12;
214
215
  let t2;
215
216
  if ($[2] !== options) {
216
217
  t2 = {
@@ -222,7 +223,7 @@ function useDocumentHistory(document, t0) {
222
223
  } else t2 = $[3];
223
224
  return useHistory(document, t2);
224
225
  }
225
- function _temp$11(a, b) {
226
+ function _temp$12(a, b) {
226
227
  if (a === b) return true;
227
228
  const aPackage = a?.package;
228
229
  const bPackage = b?.package;
@@ -743,7 +744,7 @@ const AISuggestionRectsOverlay = (t0) => {
743
744
  if (groups.length === 0) return null;
744
745
  let t1;
745
746
  if ($[0] !== groups) {
746
- t1 = groups.map(_temp$10);
747
+ t1 = groups.map(_temp$11);
747
748
  $[0] = groups;
748
749
  $[1] = t1;
749
750
  } else t1 = $[1];
@@ -759,7 +760,7 @@ const AISuggestionRectsOverlay = (t0) => {
759
760
  } else t2 = $[3];
760
761
  return t2;
761
762
  };
762
- function _temp$10(t0) {
763
+ function _temp$11(t0) {
763
764
  const { suggestion, rects, focused, font } = t0;
764
765
  const replacementAnchor = rects.at(-1);
765
766
  const showReplacement = focused && suggestion.suggestedText.length > 0 && replacementAnchor !== void 0;
@@ -994,7 +995,7 @@ const PassageHighlightOverlay = (t0) => {
994
995
  if (rects.length === 0) return null;
995
996
  let t1;
996
997
  if ($[0] !== rects) {
997
- t1 = rects.map(_temp$9);
998
+ t1 = rects.map(_temp$10);
998
999
  $[0] = rects;
999
1000
  $[1] = t1;
1000
1001
  } else t1 = $[1];
@@ -1010,7 +1011,7 @@ const PassageHighlightOverlay = (t0) => {
1010
1011
  } else t2 = $[3];
1011
1012
  return t2;
1012
1013
  };
1013
- function _temp$9(rect, idx) {
1014
+ function _temp$10(rect, idx) {
1014
1015
  return /* @__PURE__ */ jsx("span", {
1015
1016
  className: "folio-passage-highlight",
1016
1017
  style: {
@@ -2471,7 +2472,7 @@ function HfCaretOverlay(t0) {
2471
2472
  if (!pagesContainer) return null;
2472
2473
  let t4;
2473
2474
  if ($[17] !== rangeRects) {
2474
- t4 = rangeRects.map(_temp$8);
2475
+ t4 = rangeRects.map(_temp$9);
2475
2476
  $[17] = rangeRects;
2476
2477
  $[18] = t4;
2477
2478
  } else t4 = $[18];
@@ -2510,7 +2511,7 @@ function HfCaretOverlay(t0) {
2510
2511
  } else t7 = $[26];
2511
2512
  return t7;
2512
2513
  }
2513
- function _temp$8(r, i) {
2514
+ function _temp$9(r, i) {
2514
2515
  return /* @__PURE__ */ jsx("div", {
2515
2516
  "data-testid": "hf-selection-rect",
2516
2517
  style: {
@@ -7108,6 +7109,7 @@ function ContentControlWidgetsOverlay(t0) {
7108
7109
  "aria-label": t11,
7109
7110
  style,
7110
7111
  className: "bg-popover text-popover-foreground min-w-[10rem] rounded-md border p-1 shadow-md",
7112
+ onPointerDown: _temp$8,
7111
7113
  children: [t12, t13]
7112
7114
  })
7113
7115
  }), document.body);
@@ -7119,6 +7121,9 @@ function ContentControlWidgetsOverlay(t0) {
7119
7121
  } else t14 = $[31];
7120
7122
  return t14;
7121
7123
  }
7124
+ function _temp$8(event) {
7125
+ return event.stopPropagation();
7126
+ }
7122
7127
  function ContentControlDropdownItem(t0) {
7123
7128
  const $ = c(7);
7124
7129
  const { item, MenuItem, onPick } = t0;
@@ -10350,7 +10355,10 @@ const useHyperlinkHandlers = (t0) => {
10350
10355
  };
10351
10356
  function _temp$3(href) {
10352
10357
  const safeHref = sanitizeExternalUrl(href);
10353
- if (safeHref) window.open(safeHref, "_blank", "noopener,noreferrer");
10358
+ if (safeHref) {
10359
+ const anchor = anchorTargetAttrs(void 0);
10360
+ window.open(safeHref, anchor.target, windowFeaturesFor(anchor));
10361
+ }
10354
10362
  }
10355
10363
  function _temp2$2(href_0) {
10356
10364
  navigator.clipboard.writeText(href_0);
@@ -11321,7 +11329,7 @@ function areSelectionFormattingEqual(a, b) {
11321
11329
  * Edit mode: text + URL inputs with Apply.
11322
11330
  */
11323
11331
  function HyperlinkPopup(t0) {
11324
- const $ = c(88);
11332
+ const $ = c(89);
11325
11333
  const { data, onNavigate, onCopy, onEdit, onRemove, onClose, readOnly } = t0;
11326
11334
  const t = useTranslations("folio");
11327
11335
  const [mode, setMode] = useState("view");
@@ -11641,70 +11649,75 @@ function HyperlinkPopup(t0) {
11641
11649
  t15 = /* @__PURE__ */ jsx(GlobeIcon, { className: "text-muted-foreground size-4.5 shrink-0" });
11642
11650
  $[60] = t15;
11643
11651
  } else t15 = $[60];
11644
- let t16;
11652
+ const t16 = data.href;
11653
+ let t17;
11645
11654
  if ($[61] !== data.href || $[62] !== onNavigate) {
11646
- t16 = (e_6) => {
11655
+ t17 = (e_6) => {
11647
11656
  e_6.preventDefault();
11648
11657
  onNavigate(data.href);
11649
11658
  };
11650
11659
  $[61] = data.href;
11651
11660
  $[62] = onNavigate;
11652
- $[63] = t16;
11653
- } else t16 = $[63];
11654
- let t17;
11655
- if ($[64] !== data.href || $[65] !== t16) {
11656
- t17 = /* @__PURE__ */ jsx("a", {
11661
+ $[63] = t17;
11662
+ } else t17 = $[63];
11663
+ let t18;
11664
+ if ($[64] === Symbol.for("react.memo_cache_sentinel")) {
11665
+ t18 = anchorTargetAttrs(void 0);
11666
+ $[64] = t18;
11667
+ } else t18 = $[64];
11668
+ let t19;
11669
+ if ($[65] !== data.href || $[66] !== t17) {
11670
+ t19 = /* @__PURE__ */ jsx("a", {
11657
11671
  className: "text-primary truncate text-sm hover:underline",
11658
- href: data.href,
11659
- onClick: t16,
11660
- rel: "noopener noreferrer",
11661
- target: "_blank",
11672
+ href: t16,
11673
+ onClick: t17,
11674
+ ...t18,
11662
11675
  title: data.href,
11663
11676
  children: data.href
11664
11677
  });
11665
- $[64] = data.href;
11666
- $[65] = t16;
11678
+ $[65] = data.href;
11667
11679
  $[66] = t17;
11668
- } else t17 = $[66];
11669
- let t18;
11670
- if ($[67] === Symbol.for("react.memo_cache_sentinel")) {
11671
- t18 = /* @__PURE__ */ jsx("span", { className: "bg-border h-5 w-px shrink-0" });
11672
- $[67] = t18;
11673
- } else t18 = $[67];
11674
- let t19;
11675
- if ($[68] !== data.href || $[69] !== onCopy) {
11676
- t19 = () => onCopy(data.href);
11677
- $[68] = data.href;
11678
- $[69] = onCopy;
11679
- $[70] = t19;
11680
- } else t19 = $[70];
11680
+ $[67] = t19;
11681
+ } else t19 = $[67];
11681
11682
  let t20;
11682
- if ($[71] !== t) {
11683
- t20 = t("copyLink");
11684
- $[71] = t;
11685
- $[72] = t20;
11686
- } else t20 = $[72];
11683
+ if ($[68] === Symbol.for("react.memo_cache_sentinel")) {
11684
+ t20 = /* @__PURE__ */ jsx("span", { className: "bg-border h-5 w-px shrink-0" });
11685
+ $[68] = t20;
11686
+ } else t20 = $[68];
11687
11687
  let t21;
11688
- if ($[73] === Symbol.for("react.memo_cache_sentinel")) {
11689
- t21 = /* @__PURE__ */ jsx(ClipboardCopyIcon, { size: 16 });
11690
- $[73] = t21;
11691
- } else t21 = $[73];
11688
+ if ($[69] !== data.href || $[70] !== onCopy) {
11689
+ t21 = () => onCopy(data.href);
11690
+ $[69] = data.href;
11691
+ $[70] = onCopy;
11692
+ $[71] = t21;
11693
+ } else t21 = $[71];
11692
11694
  let t22;
11693
- if ($[74] !== t19 || $[75] !== t20) {
11694
- t22 = /* @__PURE__ */ jsx("button", {
11695
+ if ($[72] !== t) {
11696
+ t22 = t("copyLink");
11697
+ $[72] = t;
11698
+ $[73] = t22;
11699
+ } else t22 = $[73];
11700
+ let t23;
11701
+ if ($[74] === Symbol.for("react.memo_cache_sentinel")) {
11702
+ t23 = /* @__PURE__ */ jsx(ClipboardCopyIcon, { size: 16 });
11703
+ $[74] = t23;
11704
+ } else t23 = $[74];
11705
+ let t24;
11706
+ if ($[75] !== t21 || $[76] !== t22) {
11707
+ t24 = /* @__PURE__ */ jsx("button", {
11695
11708
  className: "flex size-7 items-center justify-center rounded text-[var(--doc-text-muted)] hover:bg-[var(--doc-bg-hover)] transition-colors",
11696
- onClick: t19,
11697
- title: t20,
11709
+ onClick: t21,
11710
+ title: t22,
11698
11711
  type: "button",
11699
- children: t21
11712
+ children: t23
11700
11713
  });
11701
- $[74] = t19;
11702
- $[75] = t20;
11714
+ $[75] = t21;
11703
11715
  $[76] = t22;
11704
- } else t22 = $[76];
11705
- let t23;
11706
- if ($[77] !== data.displayText || $[78] !== data.href || $[79] !== onRemove || $[80] !== readOnly || $[81] !== t) {
11707
- t23 = !readOnly && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("button", {
11716
+ $[77] = t24;
11717
+ } else t24 = $[77];
11718
+ let t25;
11719
+ if ($[78] !== data.displayText || $[79] !== data.href || $[80] !== onRemove || $[81] !== readOnly || $[82] !== t) {
11720
+ t25 = !readOnly && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("button", {
11708
11721
  className: "flex size-7 items-center justify-center rounded text-[var(--doc-text-muted)] hover:bg-[var(--doc-bg-hover)] transition-colors",
11709
11722
  onClick: () => {
11710
11723
  setEditText(data.displayText);
@@ -11721,16 +11734,16 @@ function HyperlinkPopup(t0) {
11721
11734
  type: "button",
11722
11735
  children: /* @__PURE__ */ jsx(UnlinkIcon, { size: 16 })
11723
11736
  })] });
11724
- $[77] = data.displayText;
11725
- $[78] = data.href;
11726
- $[79] = onRemove;
11727
- $[80] = readOnly;
11728
- $[81] = t;
11729
- $[82] = t23;
11730
- } else t23 = $[82];
11731
- let t24;
11732
- if ($[83] !== t14 || $[84] !== t17 || $[85] !== t22 || $[86] !== t23) {
11733
- t24 = /* @__PURE__ */ jsxs("div", {
11737
+ $[78] = data.displayText;
11738
+ $[79] = data.href;
11739
+ $[80] = onRemove;
11740
+ $[81] = readOnly;
11741
+ $[82] = t;
11742
+ $[83] = t25;
11743
+ } else t25 = $[83];
11744
+ let t26;
11745
+ if ($[84] !== t14 || $[85] !== t19 || $[86] !== t24 || $[87] !== t25) {
11746
+ t26 = /* @__PURE__ */ jsxs("div", {
11734
11747
  ref: popupRef,
11735
11748
  className: "fixed z-[10000] flex max-w-[400px] items-center gap-2 rounded-lg border border-[var(--doc-border)] bg-[var(--doc-page)] px-3 py-2 shadow-lg",
11736
11749
  onMouseDown: t13,
@@ -11738,19 +11751,19 @@ function HyperlinkPopup(t0) {
11738
11751
  style: t14,
11739
11752
  children: [
11740
11753
  t15,
11741
- t17,
11742
- t18,
11743
- t22,
11744
- t23
11754
+ t19,
11755
+ t20,
11756
+ t24,
11757
+ t25
11745
11758
  ]
11746
11759
  });
11747
- $[83] = t14;
11748
- $[84] = t17;
11749
- $[85] = t22;
11750
- $[86] = t23;
11751
- $[87] = t24;
11752
- } else t24 = $[87];
11753
- return t24;
11760
+ $[84] = t14;
11761
+ $[85] = t19;
11762
+ $[86] = t24;
11763
+ $[87] = t25;
11764
+ $[88] = t26;
11765
+ } else t26 = $[88];
11766
+ return t26;
11754
11767
  }
11755
11768
  function _temp2(e_5) {
11756
11769
  return e_5.stopPropagation();
@@ -12189,17 +12202,17 @@ function useFolioComments({ doc, autoOpenReviewSidebar, anchorPositions, editorC
12189
12202
  const syncCommentHighlightStyles = useCallback(() => {
12190
12203
  const root = editorContentRef.current;
12191
12204
  if (!root) return;
12192
- const nodes = root.querySelectorAll(".layout-run-text[data-comment-id]");
12205
+ const nodes = root.querySelectorAll(`.layout-run-text${COMMENT_ANCHOR_SELECTOR}`);
12193
12206
  for (const node of nodes) {
12194
- const commentId = Number.parseInt(node.dataset["commentId"] ?? "", 10);
12195
- if (!(commentId === -1 || visibleCommentIds.has(commentId))) {
12207
+ const commentIds = commentAnchorIds(node).map((id) => Number.parseInt(id, 10));
12208
+ if (!commentIds.some((commentId) => commentId === -1 || visibleCommentIds.has(commentId))) {
12196
12209
  node.style.backgroundColor = "transparent";
12197
12210
  node.style.borderBottom = "2px solid transparent";
12198
12211
  node.style.boxShadow = "none";
12199
12212
  delete node.dataset["activeComment"];
12200
12213
  continue;
12201
12214
  }
12202
- if (activeCommentId === commentId) {
12215
+ if (activeCommentId !== null && commentIds.includes(activeCommentId)) {
12203
12216
  node.style.backgroundColor = "var(--doc-comment-active-bg, rgba(255, 212, 0, 0.22))";
12204
12217
  node.style.borderBottom = "1px solid var(--doc-comment-active-border, rgba(180, 130, 0, 0.62))";
12205
12218
  node.style.boxShadow = "none";
@@ -12271,7 +12284,7 @@ function useFolioComments({ doc, autoOpenReviewSidebar, anchorPositions, editorC
12271
12284
  * - Error boundary
12272
12285
  * - Loading states
12273
12286
  */
12274
- const CommentsSidebar = lazy(() => import("./CommentsSidebar-BBbiecN2.js").then((m) => ({ default: m.CommentsSidebar })));
12287
+ const CommentsSidebar = lazy(() => import("./CommentsSidebar-BiBt77_z.js").then((m) => ({ default: m.CommentsSidebar })));
12275
12288
  const TextContextMenu = lazy(() => import("./TextContextMenu-Ci7-M4oU.js").then((m) => ({ default: m.TextContextMenu })));
12276
12289
  const loadAttemptSelectiveSave = async () => {
12277
12290
  const { attemptSelectiveSave } = await import("@stll/folio-core/docx/selectiveSave");
@@ -12533,6 +12546,7 @@ const DocxEditor = forwardRef(function DocxEditor({ documentBuffer, password, do
12533
12546
  const imageInputRef = useRef(null);
12534
12547
  const editorContentRef = useRef(null);
12535
12548
  const scrollContainerRef = useRef(null);
12549
+ const initialScrollAppliedRef = useRef(false);
12536
12550
  const [formatPainterMode, setFormatPainterModeState] = useState("off");
12537
12551
  const formatPainterModeRef = useRef("off");
12538
12552
  const capturedFormatMarksRef = useRef(null);
@@ -12680,6 +12694,11 @@ const DocxEditor = forwardRef(function DocxEditor({ documentBuffer, password, do
12680
12694
  onError,
12681
12695
  onCompatibilityChange,
12682
12696
  onReset: useCallback(() => {
12697
+ initialScrollAppliedRef.current = false;
12698
+ if (scrollContainerRef.current) {
12699
+ scrollContainerRef.current.scrollTop = 0;
12700
+ scrollContainerRef.current.scrollLeft = 0;
12701
+ }
12683
12702
  commentsDirtyRef.current = false;
12684
12703
  commentsLoadedRef.current = false;
12685
12704
  trackedChangesLoadedRef.current = false;
@@ -12747,7 +12766,6 @@ const DocxEditor = forwardRef(function DocxEditor({ documentBuffer, password, do
12747
12766
  autoOpenReviewSidebar,
12748
12767
  setShowCommentsSidebar
12749
12768
  ]);
12750
- const initialScrollAppliedRef = useRef(false);
12751
12769
  useEffect(() => {
12752
12770
  if (initialScrollTop === void 0 || state.documentLoad.status !== "ready" || initialScrollAppliedRef.current) return;
12753
12771
  initialScrollAppliedRef.current = true;
@@ -12757,7 +12775,11 @@ const DocxEditor = forwardRef(function DocxEditor({ documentBuffer, password, do
12757
12775
  return () => {
12758
12776
  cancelAnimationFrame(frame);
12759
12777
  };
12760
- }, [initialScrollTop, state.documentLoad.status]);
12778
+ }, [
12779
+ initialScrollTop,
12780
+ loadedDocumentIdentity,
12781
+ state.documentLoad.status
12782
+ ]);
12761
12783
  useEffect(() => {
12762
12784
  return onFontsLoaded(() => {
12763
12785
  onFontsLoadedCallback?.();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stll/folio-react",
3
- "version": "0.20.1",
3
+ "version": "0.20.3",
4
4
  "description": "React editor for folio: a Word-document (.docx) editor for the browser, built on @stll/folio-core and ProseMirror.",
5
5
  "keywords": [
6
6
  "document-editor",
@@ -68,7 +68,7 @@
68
68
  "@fontsource/noto-sans-arabic": "^5.2.7",
69
69
  "@fontsource/source-sans-3": "^5.2.9",
70
70
  "@fontsource/tinos": "^5.2.7",
71
- "@stll/folio-core": "^0.44.0",
71
+ "@stll/folio-core": "^0.46.0",
72
72
  "better-result": "3.0.1",
73
73
  "lucide-react": "1.24.0",
74
74
  "prosemirror-history": "^1.5.0",