@tldraw/commenting 0.0.0-bootstrap → 5.3.0-canary.31036673d3f8
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/README.md +0 -2
- package/commenting.css +1139 -329
- package/dist-cjs/canvas/anchor-lifecycle.js +148 -0
- package/dist-cjs/canvas/anchor-lifecycle.js.map +7 -0
- package/dist-cjs/canvas/cluster-input.js +2 -2
- package/dist-cjs/canvas/cluster-input.js.map +2 -2
- package/dist-cjs/canvas/comment-body.js +1 -1
- package/dist-cjs/canvas/comment-body.js.map +2 -2
- package/dist-cjs/canvas/comment-drafts.js +54 -0
- package/dist-cjs/canvas/comment-drafts.js.map +7 -0
- package/dist-cjs/canvas/comment-mutations.js +123 -0
- package/dist-cjs/canvas/comment-mutations.js.map +7 -0
- package/dist-cjs/canvas/comment-reactions.js +163 -0
- package/dist-cjs/canvas/comment-reactions.js.map +7 -0
- package/dist-cjs/canvas/comment-render.js +5 -3
- package/dist-cjs/canvas/comment-render.js.map +2 -2
- package/dist-cjs/canvas/comment-store.js +19 -9
- package/dist-cjs/canvas/comment-store.js.map +2 -2
- package/dist-cjs/canvas/comment-tool.js +64 -14
- package/dist-cjs/canvas/comment-tool.js.map +2 -2
- package/dist-cjs/canvas/comments-filter-menu.js +20 -38
- package/dist-cjs/canvas/comments-filter-menu.js.map +2 -2
- package/dist-cjs/canvas/comments-overflow-menu.js +22 -27
- package/dist-cjs/canvas/comments-overflow-menu.js.map +2 -2
- package/dist-cjs/canvas/comments-overlay.js +429 -474
- package/dist-cjs/canvas/comments-overlay.js.map +2 -2
- package/dist-cjs/canvas/comments-sidebar.js +35 -32
- package/dist-cjs/canvas/comments-sidebar.js.map +2 -2
- package/dist-cjs/canvas/comments-visibility-toggle.js +65 -0
- package/dist-cjs/canvas/comments-visibility-toggle.js.map +7 -0
- package/dist-cjs/canvas/context.js +17 -0
- package/dist-cjs/canvas/context.js.map +7 -0
- package/dist-cjs/canvas/hooks.js +22 -3
- package/dist-cjs/canvas/hooks.js.map +2 -2
- package/dist-cjs/canvas/options.js +19 -2
- package/dist-cjs/canvas/options.js.map +2 -2
- package/dist-cjs/canvas/pin-stacking.js +56 -0
- package/dist-cjs/canvas/pin-stacking.js.map +7 -0
- package/dist-cjs/canvas/sidebar-filters.js +2 -2
- package/dist-cjs/canvas/sidebar-filters.js.map +2 -2
- package/dist-cjs/canvas/state.js +28 -14
- package/dist-cjs/canvas/state.js.map +2 -2
- package/dist-cjs/canvas/thread-preview.js +185 -0
- package/dist-cjs/canvas/thread-preview.js.map +7 -0
- package/dist-cjs/canvas/thread-stack.js +199 -0
- package/dist-cjs/canvas/thread-stack.js.map +7 -0
- package/dist-cjs/canvas/thread-state.js +67 -21
- package/dist-cjs/canvas/thread-state.js.map +2 -2
- package/dist-cjs/canvas/thread-view.js +397 -0
- package/dist-cjs/canvas/thread-view.js.map +7 -0
- package/dist-cjs/clustering/computeClusterTable.js +4 -4
- package/dist-cjs/clustering/computeClusterTable.js.map +2 -2
- package/dist-cjs/clustering/runtime.js +119 -7
- package/dist-cjs/clustering/runtime.js.map +2 -2
- package/dist-cjs/clustering/types.js.map +1 -1
- package/dist-cjs/index.d.ts +763 -333
- package/dist-cjs/index.js +46 -28
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/ui/byline.js +6 -4
- package/dist-cjs/ui/byline.js.map +2 -2
- package/dist-cjs/ui/comment-card.js +15 -8
- package/dist-cjs/ui/comment-card.js.map +2 -2
- package/dist-cjs/ui/comment-composer.js +137 -36
- package/dist-cjs/ui/comment-composer.js.map +2 -2
- package/dist-cjs/ui/comment-pin.js +24 -2
- package/dist-cjs/ui/comment-pin.js.map +2 -2
- package/dist-cjs/ui/comment-thread.js +9 -7
- package/dist-cjs/ui/comment-thread.js.map +2 -2
- package/dist-cjs/ui/comments-list.js +30 -18
- package/dist-cjs/ui/comments-list.js.map +2 -2
- package/dist-cjs/ui/count-badge.js +3 -2
- package/dist-cjs/ui/count-badge.js.map +2 -2
- package/dist-cjs/ui/emoji-picker.js +54 -0
- package/dist-cjs/ui/emoji-picker.js.map +7 -0
- package/dist-cjs/ui/empty-state.js +3 -3
- package/dist-cjs/ui/empty-state.js.map +2 -2
- package/dist-cjs/ui/format-time.js +4 -1
- package/dist-cjs/ui/format-time.js.map +2 -2
- package/dist-cjs/ui/reaction-picker.js +73 -0
- package/dist-cjs/ui/reaction-picker.js.map +7 -0
- package/dist-cjs/ui/reaction.js +59 -6
- package/dist-cjs/ui/reaction.js.map +2 -2
- package/dist-cjs/ui/reactions.js +20 -7
- package/dist-cjs/ui/reactions.js.map +2 -2
- package/dist-cjs/ui/{mention.js → reply-count.js} +9 -11
- package/dist-cjs/ui/reply-count.js.map +7 -0
- package/dist-cjs/ui/send-button.js +20 -1
- package/dist-cjs/ui/send-button.js.map +2 -2
- package/dist-cjs/ui/{comment-text.js → tooltip-button.js} +12 -9
- package/dist-cjs/ui/tooltip-button.js.map +7 -0
- package/dist-esm/canvas/anchor-lifecycle.mjs +128 -0
- package/dist-esm/canvas/anchor-lifecycle.mjs.map +7 -0
- package/dist-esm/canvas/cluster-input.mjs +2 -2
- package/dist-esm/canvas/cluster-input.mjs.map +2 -2
- package/dist-esm/canvas/comment-body.mjs +1 -1
- package/dist-esm/canvas/comment-body.mjs.map +2 -2
- package/dist-esm/canvas/comment-drafts.mjs +34 -0
- package/dist-esm/canvas/comment-drafts.mjs.map +7 -0
- package/dist-esm/canvas/comment-mutations.mjs +103 -0
- package/dist-esm/canvas/comment-mutations.mjs.map +7 -0
- package/dist-esm/canvas/comment-reactions.mjs +152 -0
- package/dist-esm/canvas/comment-reactions.mjs.map +7 -0
- package/dist-esm/canvas/comment-render.mjs +5 -3
- package/dist-esm/canvas/comment-render.mjs.map +2 -2
- package/dist-esm/canvas/comment-store.mjs +19 -9
- package/dist-esm/canvas/comment-store.mjs.map +2 -2
- package/dist-esm/canvas/comment-tool.mjs +67 -17
- package/dist-esm/canvas/comment-tool.mjs.map +2 -2
- package/dist-esm/canvas/comments-filter-menu.mjs +20 -38
- package/dist-esm/canvas/comments-filter-menu.mjs.map +2 -2
- package/dist-esm/canvas/comments-overflow-menu.mjs +23 -27
- package/dist-esm/canvas/comments-overflow-menu.mjs.map +2 -2
- package/dist-esm/canvas/comments-overlay.mjs +450 -479
- package/dist-esm/canvas/comments-overlay.mjs.map +3 -3
- package/dist-esm/canvas/comments-sidebar.mjs +38 -35
- package/dist-esm/canvas/comments-sidebar.mjs.map +2 -2
- package/dist-esm/canvas/comments-visibility-toggle.mjs +45 -0
- package/dist-esm/canvas/comments-visibility-toggle.mjs.map +7 -0
- package/dist-esm/canvas/context.mjs +1 -0
- package/dist-esm/canvas/context.mjs.map +7 -0
- package/dist-esm/canvas/hooks.mjs +28 -5
- package/dist-esm/canvas/hooks.mjs.map +2 -2
- package/dist-esm/canvas/options.mjs +21 -3
- package/dist-esm/canvas/options.mjs.map +2 -2
- package/dist-esm/canvas/pin-stacking.mjs +36 -0
- package/dist-esm/canvas/pin-stacking.mjs.map +7 -0
- package/dist-esm/canvas/sidebar-filters.mjs +2 -2
- package/dist-esm/canvas/sidebar-filters.mjs.map +2 -2
- package/dist-esm/canvas/state.mjs +29 -19
- package/dist-esm/canvas/state.mjs.map +2 -2
- package/dist-esm/canvas/thread-preview.mjs +170 -0
- package/dist-esm/canvas/thread-preview.mjs.map +7 -0
- package/dist-esm/canvas/thread-stack.mjs +190 -0
- package/dist-esm/canvas/thread-stack.mjs.map +7 -0
- package/dist-esm/canvas/thread-state.mjs +71 -22
- package/dist-esm/canvas/thread-state.mjs.map +2 -2
- package/dist-esm/canvas/thread-view.mjs +402 -0
- package/dist-esm/canvas/thread-view.mjs.map +7 -0
- package/dist-esm/clustering/computeClusterTable.mjs +4 -4
- package/dist-esm/clustering/computeClusterTable.mjs.map +2 -2
- package/dist-esm/clustering/runtime.mjs +119 -7
- package/dist-esm/clustering/runtime.mjs.map +2 -2
- package/dist-esm/index.d.mts +763 -333
- package/dist-esm/index.mjs +87 -41
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/ui/byline.mjs +6 -4
- package/dist-esm/ui/byline.mjs.map +2 -2
- package/dist-esm/ui/comment-card.mjs +15 -8
- package/dist-esm/ui/comment-card.mjs.map +2 -2
- package/dist-esm/ui/comment-composer.mjs +148 -36
- package/dist-esm/ui/comment-composer.mjs.map +2 -2
- package/dist-esm/ui/comment-pin.mjs +24 -2
- package/dist-esm/ui/comment-pin.mjs.map +2 -2
- package/dist-esm/ui/comment-thread.mjs +9 -7
- package/dist-esm/ui/comment-thread.mjs.map +2 -2
- package/dist-esm/ui/comments-list.mjs +43 -29
- package/dist-esm/ui/comments-list.mjs.map +2 -2
- package/dist-esm/ui/count-badge.mjs +3 -2
- package/dist-esm/ui/count-badge.mjs.map +2 -2
- package/dist-esm/ui/emoji-picker.mjs +34 -0
- package/dist-esm/ui/emoji-picker.mjs.map +7 -0
- package/dist-esm/ui/empty-state.mjs +3 -3
- package/dist-esm/ui/empty-state.mjs.map +2 -2
- package/dist-esm/ui/format-time.mjs +4 -1
- package/dist-esm/ui/format-time.mjs.map +2 -2
- package/dist-esm/ui/reaction-picker.mjs +60 -0
- package/dist-esm/ui/reaction-picker.mjs.map +7 -0
- package/dist-esm/ui/reaction.mjs +60 -7
- package/dist-esm/ui/reaction.mjs.map +2 -2
- package/dist-esm/ui/reactions.mjs +21 -8
- package/dist-esm/ui/reactions.mjs.map +2 -2
- package/dist-esm/ui/reply-count.mjs +9 -0
- package/dist-esm/ui/reply-count.mjs.map +7 -0
- package/dist-esm/ui/send-button.mjs +20 -1
- package/dist-esm/ui/send-button.mjs.map +2 -2
- package/dist-esm/ui/tooltip-button.mjs +12 -0
- package/dist-esm/ui/tooltip-button.mjs.map +7 -0
- package/package.json +6 -6
- package/src/canvas/anchor-lifecycle.test.ts +290 -0
- package/src/canvas/anchor-lifecycle.ts +205 -0
- package/src/canvas/canvas.css +461 -46
- package/src/canvas/cluster-input.test.ts +31 -23
- package/src/canvas/cluster-input.ts +3 -4
- package/src/canvas/comment-body.tsx +1 -1
- package/src/canvas/comment-drafts.test.ts +55 -0
- package/src/canvas/comment-drafts.ts +46 -0
- package/src/canvas/comment-mutations.test.ts +410 -0
- package/src/canvas/comment-mutations.ts +288 -0
- package/src/canvas/comment-reactions.test.ts +132 -0
- package/src/canvas/comment-reactions.tsx +245 -0
- package/src/canvas/comment-render.test.ts +1 -1
- package/src/canvas/comment-render.ts +9 -5
- package/src/canvas/comment-store.ts +67 -27
- package/src/canvas/comment-tool.test.ts +162 -0
- package/src/canvas/comment-tool.tsx +105 -23
- package/src/canvas/comments-filter-menu.tsx +23 -32
- package/src/canvas/comments-overflow-menu.tsx +14 -21
- package/src/canvas/comments-overlay.tsx +695 -622
- package/src/canvas/comments-sidebar.test.ts +45 -0
- package/src/canvas/comments-sidebar.tsx +76 -58
- package/src/canvas/comments-visibility-toggle.tsx +49 -0
- package/src/canvas/context.ts +47 -0
- package/src/canvas/hooks.ts +52 -11
- package/src/canvas/options.test.ts +52 -2
- package/src/canvas/options.ts +133 -9
- package/src/canvas/pin-stacking.test.ts +114 -0
- package/src/canvas/pin-stacking.ts +57 -0
- package/src/canvas/sidebar-filters.ts +4 -3
- package/src/canvas/state.ts +92 -35
- package/src/canvas/thread-preview.test.ts +91 -0
- package/src/canvas/thread-preview.tsx +304 -0
- package/src/canvas/thread-stack.tsx +252 -0
- package/src/canvas/thread-state.test.ts +209 -0
- package/src/canvas/thread-state.ts +137 -36
- package/src/canvas/thread-view.tsx +516 -0
- package/src/clustering/computeClusterTable.test.ts +24 -23
- package/src/clustering/computeClusterTable.ts +5 -5
- package/src/clustering/runtime.test.ts +217 -7
- package/src/clustering/runtime.ts +171 -17
- package/src/clustering/types.ts +9 -9
- package/src/index.ts +76 -40
- package/src/ui/byline.tsx +7 -5
- package/src/ui/comment-card.tsx +20 -10
- package/src/ui/comment-composer.tsx +206 -40
- package/src/ui/comment-pin.tsx +26 -2
- package/src/ui/comment-thread.tsx +13 -6
- package/src/ui/comments-list.test.ts +31 -0
- package/src/ui/comments-list.tsx +45 -21
- package/src/ui/comments.css +566 -281
- package/src/ui/count-badge.tsx +9 -2
- package/src/ui/emoji-picker.test.ts +25 -0
- package/src/ui/emoji-picker.tsx +76 -0
- package/src/ui/empty-state.tsx +3 -3
- package/src/ui/format-time.ts +7 -2
- package/src/ui/reaction-picker.tsx +109 -0
- package/src/ui/reaction.tsx +121 -6
- package/src/ui/reactions.tsx +69 -10
- package/src/ui/reply-count.ts +16 -0
- package/src/ui/send-button.tsx +18 -3
- package/src/ui/tooltip-button.tsx +20 -0
- package/dist-cjs/canvas/region-options.js +0 -49
- package/dist-cjs/canvas/region-options.js.map +0 -7
- package/dist-cjs/ui/avatar.js +0 -43
- package/dist-cjs/ui/avatar.js.map +0 -7
- package/dist-cjs/ui/comment-mention.js +0 -42
- package/dist-cjs/ui/comment-mention.js.map +0 -7
- package/dist-cjs/ui/comment-text.js.map +0 -7
- package/dist-cjs/ui/mention-list.js +0 -70
- package/dist-cjs/ui/mention-list.js.map +0 -7
- package/dist-cjs/ui/mention-suggestion.js +0 -206
- package/dist-cjs/ui/mention-suggestion.js.map +0 -7
- package/dist-cjs/ui/mention.js.map +0 -7
- package/dist-cjs/ui/render-markdown.js +0 -63
- package/dist-cjs/ui/render-markdown.js.map +0 -7
- package/dist-esm/canvas/region-options.mjs +0 -29
- package/dist-esm/canvas/region-options.mjs.map +0 -7
- package/dist-esm/ui/avatar.mjs +0 -23
- package/dist-esm/ui/avatar.mjs.map +0 -7
- package/dist-esm/ui/comment-mention.mjs +0 -22
- package/dist-esm/ui/comment-mention.mjs.map +0 -7
- package/dist-esm/ui/comment-text.mjs +0 -9
- package/dist-esm/ui/comment-text.mjs.map +0 -7
- package/dist-esm/ui/mention-list.mjs +0 -50
- package/dist-esm/ui/mention-list.mjs.map +0 -7
- package/dist-esm/ui/mention-suggestion.mjs +0 -186
- package/dist-esm/ui/mention-suggestion.mjs.map +0 -7
- package/dist-esm/ui/mention.mjs +0 -11
- package/dist-esm/ui/mention.mjs.map +0 -7
- package/dist-esm/ui/render-markdown.mjs +0 -45
- package/dist-esm/ui/render-markdown.mjs.map +0 -7
- package/src/canvas/region-options.ts +0 -57
- package/src/ui/avatar.tsx +0 -31
- package/src/ui/comment-mention.ts +0 -47
- package/src/ui/comment-text.tsx +0 -12
- package/src/ui/mention-list.tsx +0 -106
- package/src/ui/mention-suggestion.test.ts +0 -18
- package/src/ui/mention-suggestion.tsx +0 -285
- package/src/ui/mention.tsx +0 -9
- package/src/ui/render-markdown.tsx +0 -72
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Avatar, isMentionPickerOpen } from "@tldraw/mentions";
|
|
2
3
|
import {
|
|
4
|
+
Fragment as Fragment2,
|
|
3
5
|
memo,
|
|
4
6
|
useCallback,
|
|
5
7
|
useEffect,
|
|
8
|
+
useLayoutEffect,
|
|
6
9
|
useMemo,
|
|
7
10
|
useRef,
|
|
8
11
|
useState
|
|
@@ -11,9 +14,7 @@ import { createPortal } from "react-dom";
|
|
|
11
14
|
import {
|
|
12
15
|
createComment,
|
|
13
16
|
createCommentThread,
|
|
14
|
-
getFirstCharacter,
|
|
15
17
|
react,
|
|
16
|
-
TldrawUiIcon,
|
|
17
18
|
useContainer,
|
|
18
19
|
useEditor,
|
|
19
20
|
usePassThroughMouseOverEvents,
|
|
@@ -23,103 +24,120 @@ import {
|
|
|
23
24
|
} from "tldraw";
|
|
24
25
|
import { computeClusterTable } from "../clustering/computeClusterTable.mjs";
|
|
25
26
|
import { createClusterRuntime } from "../clustering/runtime.mjs";
|
|
26
|
-
import { CommentCard } from "../ui/comment-card.mjs";
|
|
27
27
|
import { CommentComposer } from "../ui/comment-composer.mjs";
|
|
28
28
|
import { EMPTY_COMMENT, isCommentEmpty } from "../ui/comment-extensions.mjs";
|
|
29
29
|
import { CommentPin } from "../ui/comment-pin.mjs";
|
|
30
|
-
import { CommentThread } from "../ui/comment-thread.mjs";
|
|
31
30
|
import { CountBadge } from "../ui/count-badge.mjs";
|
|
32
|
-
import {
|
|
31
|
+
import { registerCommentAnchorLifecycle } from "./anchor-lifecycle.mjs";
|
|
33
32
|
import { collectClusterLeaves } from "./cluster-input.mjs";
|
|
34
|
-
import {
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
import {
|
|
34
|
+
clearCommentDraft,
|
|
35
|
+
getCommentDraft,
|
|
36
|
+
NEW_COMMENT_DRAFT,
|
|
37
|
+
saveCommentDraft
|
|
38
|
+
} from "./comment-drafts.mjs";
|
|
39
|
+
import { commitCommentMutation, putRecordsInCommit } from "./comment-mutations.mjs";
|
|
40
|
+
import { UNKNOWN_AUTHOR, UNKNOWN_COMMENT_AUTHOR } from "./comment-render.mjs";
|
|
41
|
+
import { getCommentRecord } from "./comment-store.mjs";
|
|
37
42
|
import { useCommentThreads, useThreadComments } from "./hooks.mjs";
|
|
38
43
|
import { useCommentingEnabled } from "./license.mjs";
|
|
39
44
|
import {
|
|
40
45
|
getCommentingOptions,
|
|
46
|
+
useCanComment,
|
|
41
47
|
useCommentingOptions
|
|
42
48
|
} from "./options.mjs";
|
|
43
|
-
import {
|
|
44
|
-
DEFAULT_REGION_COMMENT_OPTIONS,
|
|
45
|
-
setRegionCommentOptions
|
|
46
|
-
} from "./region-options.mjs";
|
|
49
|
+
import { computePinStacks, pinStackKey } from "./pin-stacking.mjs";
|
|
47
50
|
import {
|
|
48
51
|
commentsHidden,
|
|
49
|
-
|
|
52
|
+
openStackId,
|
|
50
53
|
openThreadId,
|
|
51
54
|
pendingComment,
|
|
52
55
|
regionDraft,
|
|
56
|
+
revealThreadRequest,
|
|
57
|
+
sidebarFilters,
|
|
53
58
|
toggleCommentsHidden,
|
|
54
59
|
usePendingComment
|
|
55
60
|
} from "./state.mjs";
|
|
56
|
-
import {
|
|
61
|
+
import { ThreadPreview, sortThreadsForPreview, useMarkerPreview } from "./thread-preview.mjs";
|
|
62
|
+
import { ThreadStackPin } from "./thread-stack.mjs";
|
|
63
|
+
import {
|
|
64
|
+
anchorPagePoint,
|
|
65
|
+
commentCenterScreenOffset,
|
|
66
|
+
commentTargetShapeAt,
|
|
67
|
+
impreciseShapePinInset,
|
|
68
|
+
REGION_PIN_CORNER,
|
|
69
|
+
regionAnchorPinCorner,
|
|
70
|
+
regionPinPoint,
|
|
71
|
+
shapeAnchorAt
|
|
72
|
+
} from "./thread-state.mjs";
|
|
73
|
+
import { POPOVER_OFFSET, ThreadPopover, ThreadView } from "./thread-view.mjs";
|
|
57
74
|
const stop = (e) => e.stopPropagation();
|
|
58
|
-
const
|
|
75
|
+
const isCanvasPanGesture = (editor, e) => e.button !== 0 || editor.inputs.keys.has("Space");
|
|
76
|
+
function forwardPointerEventToCanvas(container, e) {
|
|
77
|
+
const cvs = container.querySelector(".tl-canvas");
|
|
78
|
+
if (!cvs) return;
|
|
79
|
+
const newEvent = new PointerEvent(e.type, e.nativeEvent);
|
|
80
|
+
newEvent.isSpecialRedispatchedEvent = true;
|
|
81
|
+
cvs.dispatchEvent(newEvent);
|
|
82
|
+
}
|
|
59
83
|
const CLUSTER_FADE_MS = 150;
|
|
60
84
|
const CLUSTER_EXPAND_ZOOM_MS = 450;
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
viewBox: "0 0 24 24",
|
|
65
|
-
width: "15",
|
|
66
|
-
height: "15",
|
|
67
|
-
fill: "none",
|
|
68
|
-
stroke: "currentColor",
|
|
69
|
-
strokeWidth: "2",
|
|
70
|
-
strokeLinecap: "round",
|
|
71
|
-
strokeLinejoin: "round",
|
|
72
|
-
"aria-hidden": "true",
|
|
73
|
-
children: [
|
|
74
|
-
/* @__PURE__ */ jsx("path", { d: "M12 20h9" }),
|
|
75
|
-
/* @__PURE__ */ jsx("path", { d: "M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z" })
|
|
76
|
-
]
|
|
77
|
-
}
|
|
78
|
-
) });
|
|
79
|
-
function toCardProps(comment, props, components) {
|
|
80
|
-
const Body = components.CommentBody;
|
|
81
|
-
const body = Body ? /* @__PURE__ */ jsx(Body, { comment }) : /* @__PURE__ */ jsx(CommentBody, { richText: comment.body, resolveName: props.resolveName });
|
|
82
|
-
return {
|
|
83
|
-
author: props.resolveName(comment.authorId) ?? UNKNOWN_AUTHOR,
|
|
84
|
-
body,
|
|
85
|
-
date: new Date(comment.createdAt).toISOString(),
|
|
86
|
-
you: comment.authorId === props.currentUserId,
|
|
87
|
-
edited: comment.editedAt != null
|
|
88
|
-
};
|
|
89
|
-
}
|
|
85
|
+
const CLUSTER_SPLIT_ZOOM_FACTOR = 1.05;
|
|
86
|
+
const CLUSTER_CULL_MARGIN_PX = 120;
|
|
87
|
+
const THREAD_HEADER_BLOCK = 36;
|
|
90
88
|
function CanvasComments(props) {
|
|
91
89
|
const commentingEnabled = useCommentingEnabled();
|
|
92
90
|
if (!commentingEnabled) return null;
|
|
93
91
|
return /* @__PURE__ */ jsx(CanvasCommentsLayer, { ...props });
|
|
94
92
|
}
|
|
93
|
+
function useTrailingPortalHost(container) {
|
|
94
|
+
const [host, setHost] = useState(null);
|
|
95
|
+
useLayoutEffect(() => {
|
|
96
|
+
const elm = container.ownerDocument.createElement("div");
|
|
97
|
+
elm.style.display = "contents";
|
|
98
|
+
container.appendChild(elm);
|
|
99
|
+
setHost(elm);
|
|
100
|
+
return () => {
|
|
101
|
+
elm.remove();
|
|
102
|
+
setHost(null);
|
|
103
|
+
};
|
|
104
|
+
}, [container]);
|
|
105
|
+
return host;
|
|
106
|
+
}
|
|
95
107
|
function CanvasCommentsLayer(props) {
|
|
96
108
|
const editor = useEditor();
|
|
97
109
|
const options = useCommentingOptions();
|
|
98
110
|
const container = useContainer();
|
|
99
|
-
const
|
|
100
|
-
() => ({ ...DEFAULT_REGION_COMMENT_OPTIONS, ...props.regionOptions }),
|
|
101
|
-
[props.regionOptions]
|
|
102
|
-
);
|
|
103
|
-
useEffect(() => setRegionCommentOptions(editor, regionOptions), [editor, regionOptions]);
|
|
111
|
+
const portalHost = useTrailingPortalHost(container);
|
|
104
112
|
const layerRef = useRef(null);
|
|
105
|
-
usePassThroughWheelEvents(layerRef);
|
|
106
113
|
usePassThroughMouseOverEvents(layerRef);
|
|
107
|
-
const
|
|
108
|
-
const threads = useCommentThreads(editor);
|
|
114
|
+
const allThreads = useCommentThreads(editor);
|
|
109
115
|
const pending = usePendingComment();
|
|
116
|
+
const canComment = useCanComment(props.currentUserId);
|
|
117
|
+
const canRenderComposer = canComment || options.components.ComposerFallback != null;
|
|
118
|
+
const showPendingComposer = pending != null && canRenderComposer;
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
if (pending && !showPendingComposer) pendingComment.set(editor, null);
|
|
121
|
+
}, [editor, pending, showPendingComposer]);
|
|
110
122
|
const openId = useValue("open thread id", () => openThreadId.get(editor), [editor]);
|
|
111
|
-
const
|
|
112
|
-
|
|
123
|
+
const showResolved = useValue("show resolved", () => sidebarFilters.get(editor).showResolved, [
|
|
124
|
+
editor
|
|
125
|
+
]);
|
|
126
|
+
const threads = useMemo(
|
|
127
|
+
() => allThreads.filter((t) => showResolved || t.resolved == null || t.id === openId),
|
|
128
|
+
[allThreads, showResolved, openId]
|
|
129
|
+
);
|
|
130
|
+
useEffect(() => registerCommentAnchorLifecycle(editor), [editor]);
|
|
131
|
+
const [heldThreadIds, setHeldThreadIds] = useState(EMPTY_SET);
|
|
113
132
|
const adoptOnRebuild = useRef(false);
|
|
114
133
|
const clusterLeaves = useValue(
|
|
115
134
|
"comment cluster leaves",
|
|
116
135
|
() => collectClusterLeaves(
|
|
117
136
|
editor,
|
|
118
|
-
threads.filter((thread) => !
|
|
119
|
-
openThreadId.get(editor)
|
|
120
|
-
impreciseShapeAnchor
|
|
137
|
+
threads.filter((thread) => !heldThreadIds.has(thread.id)),
|
|
138
|
+
openThreadId.get(editor)
|
|
121
139
|
),
|
|
122
|
-
[editor, threads,
|
|
140
|
+
[editor, threads, heldThreadIds]
|
|
123
141
|
);
|
|
124
142
|
const clusterZoomBounds = useValue(
|
|
125
143
|
"comment cluster zoom bounds",
|
|
@@ -134,21 +152,41 @@ function CanvasCommentsLayer(props) {
|
|
|
134
152
|
}, [clusterLeaves, clusterZoomBounds, editor]);
|
|
135
153
|
const [renderedModel, setRenderedModel] = useState(latestModel);
|
|
136
154
|
let clusterModel = renderedModel;
|
|
137
|
-
|
|
155
|
+
const pageId = useValue("comment cluster page", () => editor.getCurrentPageId(), [editor]);
|
|
156
|
+
const pageRef = useRef(pageId);
|
|
157
|
+
if (pageRef.current !== pageId) {
|
|
158
|
+
pageRef.current = pageId;
|
|
159
|
+
adoptOnRebuild.current = false;
|
|
160
|
+
latestModel.runtime.seed(editor.getZoomLevel());
|
|
161
|
+
if (heldThreadIds.size > 0) setHeldThreadIds(EMPTY_SET);
|
|
162
|
+
setRenderedModel(latestModel);
|
|
163
|
+
clusterModel = latestModel;
|
|
164
|
+
}
|
|
165
|
+
const rejoinPending = heldThreadIds.size === 0 && adoptOnRebuild.current;
|
|
166
|
+
if (renderedModel !== latestModel && rejoinPending) {
|
|
138
167
|
adoptOnRebuild.current = false;
|
|
139
168
|
latestModel.runtime.seedFrom(editor.getZoomLevel(), renderedModel.runtime.getVisible());
|
|
140
169
|
setRenderedModel(latestModel);
|
|
141
170
|
clusterModel = latestModel;
|
|
171
|
+
} else if (heldThreadIds.size === 0 && renderedModel === latestModel) {
|
|
172
|
+
adoptOnRebuild.current = false;
|
|
142
173
|
}
|
|
143
174
|
const newlyMovedIds = findMovedClusteredLeafIds(clusterModel, latestModel);
|
|
144
175
|
if (newlyMovedIds.length > 0) {
|
|
145
|
-
|
|
146
|
-
const next = new Set(movedThreadIds);
|
|
176
|
+
const next = new Set(heldThreadIds);
|
|
147
177
|
for (const id of newlyMovedIds) next.add(id);
|
|
148
|
-
|
|
178
|
+
setHeldThreadIds(next);
|
|
179
|
+
}
|
|
180
|
+
{
|
|
181
|
+
const latestLeafIds = new Set(latestModel.table.leaves.map((leaf) => leaf.id));
|
|
182
|
+
for (const leaf of clusterModel.table.leaves) {
|
|
183
|
+
if (!latestLeafIds.has(leaf.id)) {
|
|
184
|
+
clusterModel.runtime.detachLeaf(leaf.id);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
149
187
|
}
|
|
150
188
|
useEffect(() => {
|
|
151
|
-
if (
|
|
189
|
+
if (heldThreadIds.size === 0) return;
|
|
152
190
|
let lastZoom = editor.getZoomLevel();
|
|
153
191
|
return react("rejoin moved comment pins on zoom out", () => {
|
|
154
192
|
const zoom = editor.getZoomLevel();
|
|
@@ -156,9 +194,9 @@ function CanvasCommentsLayer(props) {
|
|
|
156
194
|
lastZoom = zoom;
|
|
157
195
|
if (zoom >= prevZoom) return;
|
|
158
196
|
adoptOnRebuild.current = true;
|
|
159
|
-
|
|
197
|
+
setHeldThreadIds(EMPTY_SET);
|
|
160
198
|
});
|
|
161
|
-
}, [
|
|
199
|
+
}, [heldThreadIds, editor]);
|
|
162
200
|
useEffect(() => {
|
|
163
201
|
if (clusterModel === latestModel) return;
|
|
164
202
|
let lastZoom = editor.getZoomLevel();
|
|
@@ -171,90 +209,113 @@ function CanvasCommentsLayer(props) {
|
|
|
171
209
|
setRenderedModel(latestModel);
|
|
172
210
|
});
|
|
173
211
|
}, [clusterModel, latestModel, editor]);
|
|
212
|
+
const partitionVersion = clusterModel.runtime.version;
|
|
174
213
|
const orphanThreads = useMemo(() => {
|
|
175
214
|
if (clusterModel === latestModel) return [];
|
|
176
|
-
const
|
|
215
|
+
const displayed = /* @__PURE__ */ new Set();
|
|
216
|
+
for (const node of clusterModel.runtime.getVisible().values()) {
|
|
217
|
+
for (const member of node.members) displayed.add(member);
|
|
218
|
+
}
|
|
177
219
|
const latestIds = new Set(latestModel.table.leaves.map((leaf) => leaf.id));
|
|
178
|
-
return threads.filter((thread) => latestIds.has(thread.id) && !
|
|
179
|
-
}, [clusterModel, latestModel, threads]);
|
|
180
|
-
const
|
|
181
|
-
() => threads.filter((thread) =>
|
|
182
|
-
[threads,
|
|
220
|
+
return threads.filter((thread) => latestIds.has(thread.id) && !displayed.has(thread.id));
|
|
221
|
+
}, [clusterModel, latestModel, threads, partitionVersion]);
|
|
222
|
+
const heldThreads = useMemo(
|
|
223
|
+
() => threads.filter((thread) => heldThreadIds.has(thread.id) && thread.id !== openId),
|
|
224
|
+
[threads, heldThreadIds, openId]
|
|
183
225
|
);
|
|
184
|
-
|
|
185
|
-
"comment cluster
|
|
226
|
+
useValue(
|
|
227
|
+
"comment cluster version",
|
|
186
228
|
() => {
|
|
187
229
|
clusterModel.runtime.onCamera(editor.getZoomLevel());
|
|
188
|
-
return clusterModel.runtime.
|
|
230
|
+
return clusterModel.runtime.version;
|
|
189
231
|
},
|
|
190
232
|
[clusterModel, editor]
|
|
191
233
|
);
|
|
192
234
|
const visibleNodes = useMemo(() => {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
`[comments] cluster cursor k=${clusterCursor} \u2192 re-rendering ${nodes.length} visible nodes`
|
|
196
|
-
);
|
|
197
|
-
return nodes;
|
|
198
|
-
}, [clusterModel, clusterCursor]);
|
|
235
|
+
return Array.from(clusterModel.runtime.getVisible().values());
|
|
236
|
+
}, [clusterModel, partitionVersion]);
|
|
199
237
|
const fadeNodes = useFadeVisibleNodes(visibleNodes, clusterModel);
|
|
200
238
|
const threadsById = useMemo(
|
|
201
239
|
() => new Map(threads.map((thread) => [thread.id, thread])),
|
|
202
240
|
[threads]
|
|
203
241
|
);
|
|
242
|
+
const pinStacks = useValue("comment pin stacks", () => computePinStacks(editor, threads), [
|
|
243
|
+
editor,
|
|
244
|
+
threads
|
|
245
|
+
]);
|
|
204
246
|
const openThread = openId ? threadsById.get(openId) : null;
|
|
205
247
|
const hidden = useValue("comments hidden", () => commentsHidden.get(editor), [editor]);
|
|
206
248
|
useEffect(() => {
|
|
207
249
|
return () => {
|
|
208
250
|
openThreadId.set(editor, null);
|
|
251
|
+
openStackId.set(editor, null);
|
|
209
252
|
pendingComment.set(editor, null);
|
|
253
|
+
revealThreadRequest.set(editor, null);
|
|
210
254
|
};
|
|
211
255
|
}, [editor]);
|
|
212
256
|
useEffect(() => {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
if (!record) return;
|
|
221
|
-
let thread;
|
|
222
|
-
if (record.typeName === "comment") {
|
|
223
|
-
thread = threadsById.get(record.threadId);
|
|
224
|
-
} else {
|
|
225
|
-
thread = record;
|
|
257
|
+
const key = openStackId.get(editor);
|
|
258
|
+
if (!key) return;
|
|
259
|
+
for (const id of pinStacks.keys()) {
|
|
260
|
+
const thread = threadsById.get(id);
|
|
261
|
+
if (!thread) continue;
|
|
262
|
+
const point = anchorPagePoint(editor, thread.anchor);
|
|
263
|
+
if (point && pinStackKey(point) === key) return;
|
|
226
264
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
265
|
+
openStackId.set(editor, null);
|
|
266
|
+
}, [editor, pinStacks, threadsById]);
|
|
267
|
+
const requestedRevealThread = useValue(
|
|
268
|
+
"requested reveal thread",
|
|
269
|
+
() => {
|
|
270
|
+
const id = revealThreadRequest.get(editor);
|
|
271
|
+
if (!id) return null;
|
|
272
|
+
const record = getCommentRecord(editor, id);
|
|
273
|
+
if (!record) return null;
|
|
274
|
+
const thread = record.typeName === "comment" ? getCommentRecord(editor, record.threadId) : record;
|
|
275
|
+
return thread?.typeName === "comment-thread" ? thread : null;
|
|
276
|
+
},
|
|
277
|
+
[editor]
|
|
278
|
+
);
|
|
279
|
+
useEffect(() => {
|
|
280
|
+
if (!requestedRevealThread) return;
|
|
281
|
+
revealThreadRequest.set(editor, null);
|
|
282
|
+
commentsHidden.set(editor, false);
|
|
283
|
+
revealThreadPin(editor, requestedRevealThread, clusterModel.table, clusterZoomBounds, options);
|
|
284
|
+
openThreadId.set(editor, requestedRevealThread.id);
|
|
285
|
+
}, [requestedRevealThread, clusterModel.table, clusterZoomBounds, editor, options]);
|
|
286
|
+
const revealClusteredThread = useCallback(
|
|
287
|
+
(thread) => {
|
|
288
|
+
revealThreadPin(
|
|
289
|
+
editor,
|
|
290
|
+
thread,
|
|
291
|
+
clusterModel.table,
|
|
292
|
+
clusterZoomBounds,
|
|
293
|
+
options,
|
|
294
|
+
CLUSTER_EXPAND_ZOOM_MS
|
|
295
|
+
);
|
|
296
|
+
openThreadId.set(editor, thread.id);
|
|
297
|
+
},
|
|
298
|
+
[clusterModel.table, clusterZoomBounds, editor, options]
|
|
299
|
+
);
|
|
239
300
|
const zoomToClusterSplit = useCallback(
|
|
240
301
|
(node) => {
|
|
241
302
|
const event = clusterModel.table.events.find((e) => e.result.id === node.id);
|
|
242
303
|
if (!event || !Number.isFinite(event.zSplit)) return;
|
|
243
304
|
const zoom = clamp(
|
|
244
|
-
event.zSplit *
|
|
305
|
+
event.zSplit * CLUSTER_SPLIT_ZOOM_FACTOR,
|
|
245
306
|
clusterZoomBounds.minZoom,
|
|
246
307
|
clusterZoomBounds.maxZoom
|
|
247
308
|
);
|
|
248
309
|
centerOnPointAtZoom(editor, node.centroid, zoom, CLUSTER_EXPAND_ZOOM_MS);
|
|
249
310
|
},
|
|
250
|
-
[clusterModel, clusterZoomBounds, editor
|
|
311
|
+
[clusterModel, clusterZoomBounds, editor]
|
|
251
312
|
);
|
|
252
313
|
useEffect(() => {
|
|
253
314
|
const onKeyDown = (e) => {
|
|
254
315
|
if (e.key !== "Escape" || openThreadId.get(editor) === null) return;
|
|
255
316
|
if (isMentionPickerOpen()) return;
|
|
256
317
|
const target = e.target;
|
|
257
|
-
if (target && target.closest(".cmt-editing")) return;
|
|
318
|
+
if (target && target.closest(".tlui-cmt-editing")) return;
|
|
258
319
|
openThreadId.set(editor, null);
|
|
259
320
|
e.preventDefault();
|
|
260
321
|
e.stopPropagation();
|
|
@@ -274,79 +335,80 @@ function CanvasCommentsLayer(props) {
|
|
|
274
335
|
return () => document.removeEventListener("keydown", onKeyDown, true);
|
|
275
336
|
}, [editor]);
|
|
276
337
|
if (hidden) return null;
|
|
338
|
+
const stackGroupOf = (node) => {
|
|
339
|
+
const group = pinStacks.get(node.members[0]);
|
|
340
|
+
if (!group) return null;
|
|
341
|
+
return node.members.every((id) => group.includes(id)) ? group : null;
|
|
342
|
+
};
|
|
343
|
+
const renderedThreadIds = /* @__PURE__ */ new Set();
|
|
344
|
+
if (options.enableClustering) {
|
|
345
|
+
for (const { node } of fadeNodes) {
|
|
346
|
+
if (node.count === 1) renderedThreadIds.add(node.id);
|
|
347
|
+
else if (stackGroupOf(node)) for (const id of node.members) renderedThreadIds.add(id);
|
|
348
|
+
}
|
|
349
|
+
for (const thread of orphanThreads) renderedThreadIds.add(thread.id);
|
|
350
|
+
for (const thread of heldThreads) renderedThreadIds.add(thread.id);
|
|
351
|
+
} else {
|
|
352
|
+
for (const thread of threads) renderedThreadIds.add(thread.id);
|
|
353
|
+
}
|
|
354
|
+
if (openThread) renderedThreadIds.add(openThread.id);
|
|
355
|
+
const renderThreadPin = (thread) => {
|
|
356
|
+
const group = pinStacks.get(thread.id);
|
|
357
|
+
if (group) {
|
|
358
|
+
const owner = group.find((id) => renderedThreadIds.has(id));
|
|
359
|
+
if (owner !== thread.id) return null;
|
|
360
|
+
const stackThreads = group.map((id) => threadsById.get(id)).filter((t) => t !== void 0);
|
|
361
|
+
return /* @__PURE__ */ jsx(ThreadStackPin, { editor, threads: stackThreads, ...props });
|
|
362
|
+
}
|
|
363
|
+
return /* @__PURE__ */ jsx(ThreadPin, { editor, thread, ...props });
|
|
364
|
+
};
|
|
365
|
+
if (!portalHost) return null;
|
|
277
366
|
return createPortal(
|
|
278
|
-
/* @__PURE__ */ jsxs("div", { ref: layerRef, className: "cmt-canvas-layer", children: [
|
|
367
|
+
/* @__PURE__ */ jsxs("div", { ref: layerRef, className: "tlui-cmt-canvas-layer", children: [
|
|
279
368
|
options.enableClustering ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
280
369
|
fadeNodes.map(({ node, phase }) => {
|
|
281
370
|
let content;
|
|
371
|
+
const stackGroup = node.count > 1 ? stackGroupOf(node) : null;
|
|
282
372
|
if (node.count === 1) {
|
|
283
373
|
const thread = threadsById.get(node.id);
|
|
284
374
|
if (!thread) return null;
|
|
375
|
+
content = renderThreadPin(thread);
|
|
376
|
+
} else if (stackGroup) {
|
|
377
|
+
const owner = stackGroup.find((id) => renderedThreadIds.has(id));
|
|
378
|
+
content = owner && node.members.includes(owner) ? renderThreadPin(threadsById.get(owner)) : null;
|
|
379
|
+
} else {
|
|
285
380
|
content = /* @__PURE__ */ jsx(
|
|
286
|
-
|
|
381
|
+
ClusterBadge,
|
|
287
382
|
{
|
|
288
383
|
editor,
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
384
|
+
node,
|
|
385
|
+
onExpand: zoomToClusterSplit,
|
|
386
|
+
onSelectThread: revealClusteredThread,
|
|
387
|
+
threadsById,
|
|
388
|
+
currentUserId: props.currentUserId,
|
|
389
|
+
resolveAuthor: props.resolveAuthor
|
|
292
390
|
}
|
|
293
391
|
);
|
|
294
|
-
} else {
|
|
295
|
-
content = /* @__PURE__ */ jsx(ClusterBadge, { editor, node, onExpand: zoomToClusterSplit });
|
|
296
392
|
}
|
|
297
393
|
return /* @__PURE__ */ jsx("div", { className: clusterFadeClassName(phase), children: content }, `cluster-fade:${node.id}`);
|
|
298
394
|
}),
|
|
299
|
-
orphanThreads.map((thread) => /* @__PURE__ */ jsx(
|
|
300
|
-
|
|
301
|
-
{
|
|
302
|
-
editor,
|
|
303
|
-
thread,
|
|
304
|
-
...props,
|
|
305
|
-
regionOptions
|
|
306
|
-
},
|
|
307
|
-
thread.id
|
|
308
|
-
)),
|
|
309
|
-
movedThreads.map((thread) => /* @__PURE__ */ jsx(
|
|
310
|
-
ThreadPin,
|
|
311
|
-
{
|
|
312
|
-
editor,
|
|
313
|
-
thread,
|
|
314
|
-
...props,
|
|
315
|
-
regionOptions
|
|
316
|
-
},
|
|
317
|
-
thread.id
|
|
318
|
-
))
|
|
395
|
+
orphanThreads.map((thread) => /* @__PURE__ */ jsx(Fragment2, { children: renderThreadPin(thread) }, thread.id)),
|
|
396
|
+
heldThreads.map((thread) => /* @__PURE__ */ jsx(Fragment2, { children: renderThreadPin(thread) }, thread.id))
|
|
319
397
|
] }) : (
|
|
320
398
|
// Clustering off: every thread renders as its own live pin (each returns null when it's
|
|
321
399
|
// not on the current page or its anchor is missing). The open thread is excluded here and
|
|
322
400
|
// rendered once below, mirroring how the clustering path keeps it out of the cluster leaves —
|
|
323
401
|
// otherwise it would mount a second, stacked pin.
|
|
324
|
-
(threads.filter((thread) => thread.id !== openId).map(
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
editor,
|
|
328
|
-
thread,
|
|
329
|
-
...props,
|
|
330
|
-
regionOptions
|
|
331
|
-
},
|
|
332
|
-
thread.id
|
|
333
|
-
)))
|
|
334
|
-
),
|
|
335
|
-
openThread && /* @__PURE__ */ jsx(
|
|
336
|
-
ThreadPin,
|
|
337
|
-
{
|
|
338
|
-
editor,
|
|
339
|
-
thread: openThread,
|
|
340
|
-
...props,
|
|
341
|
-
regionOptions
|
|
342
|
-
},
|
|
343
|
-
`open:${openThread.id}`
|
|
402
|
+
(threads.filter((thread) => thread.id !== openId).map(
|
|
403
|
+
(thread) => /* @__PURE__ */ jsx(Fragment2, { children: renderThreadPin(thread) }, thread.id)
|
|
404
|
+
))
|
|
344
405
|
),
|
|
406
|
+
openThread && /* @__PURE__ */ jsx(Fragment2, { children: renderThreadPin(openThread) }, `open:${openThread.id}`),
|
|
345
407
|
/* @__PURE__ */ jsx(RegionDraftBox, { editor }),
|
|
346
|
-
pending?.anchor.type === "region" && /* @__PURE__ */ jsx(RegionBox, { editor, box: pending.anchor }),
|
|
347
|
-
pending &&
|
|
408
|
+
pending?.anchor.type === "region" && showPendingComposer && /* @__PURE__ */ jsx(RegionBox, { editor, box: pending.anchor }),
|
|
409
|
+
pending && showPendingComposer && /* @__PURE__ */ jsx(PendingComposer, { editor, pending, ...props })
|
|
348
410
|
] }),
|
|
349
|
-
|
|
411
|
+
portalHost
|
|
350
412
|
);
|
|
351
413
|
}
|
|
352
414
|
const EMPTY_SET = /* @__PURE__ */ new Set();
|
|
@@ -415,7 +477,7 @@ function cancelClusterFadeFrame(frame) {
|
|
|
415
477
|
else window.clearTimeout(frame);
|
|
416
478
|
}
|
|
417
479
|
function clusterFadeClassName(phase) {
|
|
418
|
-
return `cmt-cluster-fade cmt-cluster-fade--${phase}`;
|
|
480
|
+
return `tlui-cmt-cluster-fade tlui-cmt-cluster-fade--${phase}`;
|
|
419
481
|
}
|
|
420
482
|
function findMovedClusteredLeafIds(rendered, latest) {
|
|
421
483
|
if (rendered.table === latest.table) return [];
|
|
@@ -432,11 +494,6 @@ function findMovedClusteredLeafIds(rendered, latest) {
|
|
|
432
494
|
}
|
|
433
495
|
return moved;
|
|
434
496
|
}
|
|
435
|
-
function hasRemovedLeaves(rendered, latest) {
|
|
436
|
-
if (rendered.leaves.length === 0) return false;
|
|
437
|
-
const latestIds = new Set(latest.leaves.map((leaf) => leaf.id));
|
|
438
|
-
return rendered.leaves.some((leaf) => !latestIds.has(leaf.id));
|
|
439
|
-
}
|
|
440
497
|
function getClusterZoomBounds(editor) {
|
|
441
498
|
const cameraOptions = editor.getCameraOptions();
|
|
442
499
|
const baseZoom = cameraOptions.constraints ? editor.getBaseZoom() : 1;
|
|
@@ -446,34 +503,36 @@ function getClusterZoomBounds(editor) {
|
|
|
446
503
|
maxZoom: zoomSteps[zoomSteps.length - 1] * baseZoom
|
|
447
504
|
};
|
|
448
505
|
}
|
|
449
|
-
function
|
|
506
|
+
function revealThreadPin(editor, thread, table, zoomBounds, options, duration = 200) {
|
|
450
507
|
if (thread.pageId !== editor.getCurrentPageId()) {
|
|
451
508
|
editor.setCurrentPage(thread.pageId);
|
|
452
509
|
}
|
|
453
|
-
const point = anchorPagePoint(editor, thread.anchor
|
|
510
|
+
const point = anchorPagePoint(editor, thread.anchor);
|
|
454
511
|
if (!point) return;
|
|
455
512
|
if (options.enableClustering) {
|
|
456
513
|
const parentEvent = findDirectParentEvent(table, thread.id);
|
|
457
514
|
if (parentEvent && Number.isFinite(parentEvent.zSplit) && parentEvent.zSplit <= zoomBounds.maxZoom) {
|
|
458
515
|
const zoom = clamp(
|
|
459
|
-
parentEvent.zSplit *
|
|
516
|
+
parentEvent.zSplit * CLUSTER_SPLIT_ZOOM_FACTOR,
|
|
460
517
|
zoomBounds.minZoom,
|
|
461
518
|
zoomBounds.maxZoom
|
|
462
519
|
);
|
|
463
|
-
centerOnPointAtZoom(editor, point, zoom);
|
|
520
|
+
centerOnPointAtZoom(editor, point, zoom, duration);
|
|
464
521
|
return;
|
|
465
522
|
}
|
|
466
523
|
}
|
|
467
|
-
|
|
524
|
+
const offset = commentCenterScreenOffset(editor) / editor.getZoomLevel();
|
|
525
|
+
editor.centerOnPoint({ x: point.x + offset, y: point.y }, { animation: { duration } });
|
|
468
526
|
}
|
|
469
527
|
function findDirectParentEvent(table, threadId) {
|
|
470
528
|
return table.events.find((event) => event.children.some((child) => child.id === threadId));
|
|
471
529
|
}
|
|
472
530
|
function centerOnPointAtZoom(editor, point, zoom, duration = 200) {
|
|
473
531
|
const viewport = editor.getViewportScreenBounds();
|
|
532
|
+
const offset = commentCenterScreenOffset(editor);
|
|
474
533
|
editor.setCamera(
|
|
475
534
|
{
|
|
476
|
-
x: viewport.w /
|
|
535
|
+
x: (viewport.w / 2 - offset) / zoom - point.x,
|
|
477
536
|
y: viewport.h / (2 * zoom) - point.y,
|
|
478
537
|
z: zoom
|
|
479
538
|
},
|
|
@@ -486,8 +545,16 @@ function clamp(value, min, max) {
|
|
|
486
545
|
const ClusterBadge = memo(function ClusterBadge2({
|
|
487
546
|
editor,
|
|
488
547
|
node,
|
|
489
|
-
onExpand
|
|
548
|
+
onExpand,
|
|
549
|
+
onSelectThread,
|
|
550
|
+
threadsById,
|
|
551
|
+
...props
|
|
490
552
|
}) {
|
|
553
|
+
const container = useContainer();
|
|
554
|
+
const msg = useTranslation();
|
|
555
|
+
const badgeRef = useRef(null);
|
|
556
|
+
const { previewShown, previewHandlers } = useMarkerPreview(editor, `cluster:${node.id}`);
|
|
557
|
+
usePassThroughWheelEvents(badgeRef);
|
|
491
558
|
const point = useValue(
|
|
492
559
|
"cluster badge point",
|
|
493
560
|
() => {
|
|
@@ -497,46 +564,63 @@ const ClusterBadge = memo(function ClusterBadge2({
|
|
|
497
564
|
},
|
|
498
565
|
[editor, node]
|
|
499
566
|
);
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
{
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
onPointerDown: stop,
|
|
507
|
-
onClick: (e) => {
|
|
508
|
-
e.stopPropagation();
|
|
509
|
-
onExpand(node);
|
|
510
|
-
},
|
|
511
|
-
children: /* @__PURE__ */ jsx(CountBadge, { count: node.count })
|
|
567
|
+
const previewThreads = useMemo(() => {
|
|
568
|
+
if (!previewShown) return [];
|
|
569
|
+
const threads = [];
|
|
570
|
+
for (const id of node.members) {
|
|
571
|
+
const thread = threadsById.get(id);
|
|
572
|
+
if (thread) threads.push(thread);
|
|
512
573
|
}
|
|
513
|
-
|
|
574
|
+
return sortThreadsForPreview(threads);
|
|
575
|
+
}, [previewShown, node.members, threadsById]);
|
|
576
|
+
if (!point) return null;
|
|
577
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
578
|
+
/* @__PURE__ */ jsx(
|
|
579
|
+
"button",
|
|
580
|
+
{
|
|
581
|
+
ref: badgeRef,
|
|
582
|
+
type: "button",
|
|
583
|
+
className: "tlui-cmt-button tlui-cmt-canvas-cluster",
|
|
584
|
+
style: { left: point.x, top: point.y },
|
|
585
|
+
"aria-label": msg("comments.cluster-label").replace("{count}", String(node.count)),
|
|
586
|
+
onPointerDown: (e) => {
|
|
587
|
+
if (isCanvasPanGesture(editor, e)) {
|
|
588
|
+
forwardPointerEventToCanvas(container, e);
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
e.stopPropagation();
|
|
592
|
+
},
|
|
593
|
+
onClick: (e) => {
|
|
594
|
+
e.stopPropagation();
|
|
595
|
+
onExpand(node);
|
|
596
|
+
},
|
|
597
|
+
...previewHandlers,
|
|
598
|
+
onFocus: previewHandlers.onPointerEnter,
|
|
599
|
+
onBlur: previewHandlers.onPointerLeave,
|
|
600
|
+
children: /* @__PURE__ */ jsx(CountBadge, { count: node.count })
|
|
601
|
+
}
|
|
602
|
+
),
|
|
603
|
+
previewShown && previewThreads.length > 0 && /* @__PURE__ */ jsx(
|
|
604
|
+
ThreadPreview,
|
|
605
|
+
{
|
|
606
|
+
editor,
|
|
607
|
+
threads: previewThreads,
|
|
608
|
+
container,
|
|
609
|
+
variant: "list",
|
|
610
|
+
point,
|
|
611
|
+
onSelectThread,
|
|
612
|
+
...previewHandlers,
|
|
613
|
+
...props
|
|
614
|
+
}
|
|
615
|
+
)
|
|
616
|
+
] });
|
|
514
617
|
});
|
|
515
618
|
function isInInflatedViewport(editor, point) {
|
|
516
619
|
const viewport = editor.getViewportScreenBounds();
|
|
517
|
-
const margin =
|
|
620
|
+
const margin = CLUSTER_CULL_MARGIN_PX;
|
|
518
621
|
return point.x >= -margin && point.y >= -margin && point.x <= viewport.w + margin && point.y <= viewport.h + margin;
|
|
519
622
|
}
|
|
520
|
-
function
|
|
521
|
-
container,
|
|
522
|
-
style,
|
|
523
|
-
children
|
|
524
|
-
}) {
|
|
525
|
-
const ref = useRef(null);
|
|
526
|
-
usePassThroughWheelEvents(ref);
|
|
527
|
-
usePassThroughMouseOverEvents(ref);
|
|
528
|
-
return createPortal(
|
|
529
|
-
/* @__PURE__ */ jsx("div", { ref, className: "cmt-canvas-popover", style, onPointerDown: stop, children }),
|
|
530
|
-
container
|
|
531
|
-
);
|
|
532
|
-
}
|
|
533
|
-
function RegionBox({
|
|
534
|
-
editor,
|
|
535
|
-
box,
|
|
536
|
-
movable,
|
|
537
|
-
onPreview,
|
|
538
|
-
onCommit
|
|
539
|
-
}) {
|
|
623
|
+
function RegionBox({ editor, box }) {
|
|
540
624
|
const rect = useValue(
|
|
541
625
|
"region rect",
|
|
542
626
|
() => {
|
|
@@ -546,38 +630,7 @@ function RegionBox({
|
|
|
546
630
|
},
|
|
547
631
|
[editor, box.x, box.y, box.w, box.h]
|
|
548
632
|
);
|
|
549
|
-
|
|
550
|
-
const translated = (e) => {
|
|
551
|
-
const g = grabRef.current;
|
|
552
|
-
const p = editor.screenToPage({ x: e.clientX, y: e.clientY });
|
|
553
|
-
return { ...g.box, x: g.box.x + (p.x - g.page.x), y: g.box.y + (p.y - g.page.y) };
|
|
554
|
-
};
|
|
555
|
-
const startMove = (e) => {
|
|
556
|
-
e.stopPropagation();
|
|
557
|
-
grabRef.current = { page: editor.screenToPage({ x: e.clientX, y: e.clientY }), box };
|
|
558
|
-
e.currentTarget.setPointerCapture(e.pointerId);
|
|
559
|
-
};
|
|
560
|
-
const onMove = (e) => {
|
|
561
|
-
if (grabRef.current) onPreview?.(translated(e));
|
|
562
|
-
};
|
|
563
|
-
const endMove = (e) => {
|
|
564
|
-
if (!grabRef.current) return;
|
|
565
|
-
const bounds = translated(e);
|
|
566
|
-
grabRef.current = null;
|
|
567
|
-
if (e.currentTarget.hasPointerCapture(e.pointerId))
|
|
568
|
-
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
569
|
-
onCommit?.(bounds);
|
|
570
|
-
};
|
|
571
|
-
return /* @__PURE__ */ jsx(
|
|
572
|
-
"div",
|
|
573
|
-
{
|
|
574
|
-
className: movable ? "cmt-canvas-region cmt-canvas-region--movable" : "cmt-canvas-region",
|
|
575
|
-
style: rect,
|
|
576
|
-
onPointerDown: movable ? startMove : void 0,
|
|
577
|
-
onPointerMove: movable ? onMove : void 0,
|
|
578
|
-
onPointerUp: movable ? endMove : void 0
|
|
579
|
-
}
|
|
580
|
-
);
|
|
633
|
+
return /* @__PURE__ */ jsx("div", { className: "tlui-cmt-canvas-region", style: rect });
|
|
581
634
|
}
|
|
582
635
|
function RegionDraftBox({ editor }) {
|
|
583
636
|
const box = useValue("region draft", () => regionDraft.get(editor), [editor]);
|
|
@@ -590,12 +643,6 @@ const REGION_CORNERS = [
|
|
|
590
643
|
{ x: 0, y: 1, cursor: "nesw-resize" },
|
|
591
644
|
{ x: 1, y: 1, cursor: "nwse-resize" }
|
|
592
645
|
];
|
|
593
|
-
const REGION_EDGES = [
|
|
594
|
-
{ x: 0.5, y: 0, cursor: "ns-resize" },
|
|
595
|
-
{ x: 1, y: 0.5, cursor: "ew-resize" },
|
|
596
|
-
{ x: 0.5, y: 1, cursor: "ns-resize" },
|
|
597
|
-
{ x: 0, y: 0.5, cursor: "ew-resize" }
|
|
598
|
-
];
|
|
599
646
|
const REGION_HANDLE_MARGIN_PX = 12;
|
|
600
647
|
function resizeRegion(box, handle, cursor) {
|
|
601
648
|
const controlsX = handle.x !== 0.5;
|
|
@@ -645,7 +692,7 @@ function RegionResizeHandles({
|
|
|
645
692
|
return /* @__PURE__ */ jsx(Fragment, { children: points.map((h) => /* @__PURE__ */ jsx(
|
|
646
693
|
"div",
|
|
647
694
|
{
|
|
648
|
-
className: "cmt-canvas-region-handle",
|
|
695
|
+
className: "tlui-cmt-canvas-region-handle",
|
|
649
696
|
style: { left: h.left, top: h.top, cursor: h.cursor },
|
|
650
697
|
onPointerDown: startResize,
|
|
651
698
|
onPointerMove: onResize(h),
|
|
@@ -657,33 +704,22 @@ function RegionResizeHandles({
|
|
|
657
704
|
const ThreadPin = memo(function ThreadPin2({
|
|
658
705
|
editor,
|
|
659
706
|
thread,
|
|
660
|
-
regionOptions,
|
|
661
707
|
...props
|
|
662
708
|
}) {
|
|
663
|
-
const {
|
|
664
|
-
currentUserId,
|
|
665
|
-
resolveName,
|
|
666
|
-
onPostComment,
|
|
667
|
-
isCommentUnread,
|
|
668
|
-
onCommentRead,
|
|
669
|
-
getMentionSuggestions,
|
|
670
|
-
renderMentionSuggestion
|
|
671
|
-
} = props;
|
|
709
|
+
const { resolveAuthor } = props;
|
|
672
710
|
const options = useCommentingOptions();
|
|
673
|
-
const
|
|
711
|
+
const canComment = useCanComment(props.currentUserId);
|
|
674
712
|
const container = useContainer();
|
|
675
|
-
const comments = useThreadComments(editor, thread.id);
|
|
676
713
|
const msg = useTranslation();
|
|
714
|
+
const comments = useThreadComments(editor, thread.id);
|
|
677
715
|
const open = useValue("thread open", () => openThreadId.get(editor) === thread.id, [
|
|
678
716
|
editor,
|
|
679
717
|
thread.id
|
|
680
718
|
]);
|
|
681
|
-
const [reply, setReply] = useState(EMPTY_COMMENT);
|
|
682
|
-
const [editingId, setEditingId] = useState(null);
|
|
683
|
-
const [editText, setEditText] = useState(EMPTY_COMMENT);
|
|
684
719
|
const [dragPagePoint, setDragPagePoint] = useState(null);
|
|
685
720
|
const [resizeBounds, setResizeBounds] = useState(null);
|
|
686
|
-
const
|
|
721
|
+
const { previewShown, previewHandlers } = useMarkerPreview(editor, `pin:${thread.id}`);
|
|
722
|
+
const previewThreads = useMemo(() => [thread], [thread]);
|
|
687
723
|
const pointerInRegion = useValue(
|
|
688
724
|
"pointer in region",
|
|
689
725
|
() => {
|
|
@@ -696,25 +732,30 @@ const ThreadPin = memo(function ThreadPin2({
|
|
|
696
732
|
},
|
|
697
733
|
[editor, thread.anchor, thread.pageId]
|
|
698
734
|
);
|
|
699
|
-
const revealed = open || resizeBounds != null ||
|
|
735
|
+
const revealed = open || resizeBounds != null || pointerInRegion;
|
|
736
|
+
const pinCorner = thread.anchor.type === "region" ? regionAnchorPinCorner(thread.anchor) : REGION_PIN_CORNER;
|
|
700
737
|
const resizeHandles = useMemo(
|
|
701
|
-
() =>
|
|
702
|
-
|
|
703
|
-
),
|
|
704
|
-
[regionOptions.resize, regionOptions.pinCorner]
|
|
738
|
+
() => REGION_CORNERS.filter((c) => c.x !== pinCorner.x || c.y !== pinCorner.y),
|
|
739
|
+
[pinCorner]
|
|
705
740
|
);
|
|
706
741
|
const dragRef = useRef(null);
|
|
707
742
|
const markerRef = useRef(null);
|
|
743
|
+
usePassThroughWheelEvents(markerRef);
|
|
744
|
+
useEffect(() => {
|
|
745
|
+
return () => {
|
|
746
|
+
if (dragRef.current) editor.setHintingShapes([]);
|
|
747
|
+
};
|
|
748
|
+
}, [editor]);
|
|
708
749
|
useEffect(() => {
|
|
709
750
|
if (!open) return;
|
|
710
751
|
const onPointerDown = (e) => {
|
|
711
752
|
const target = e.target;
|
|
712
753
|
if (!target) return;
|
|
713
|
-
if (target.closest(".cmt-canvas-popover")) return;
|
|
754
|
+
if (target.closest(".tlui-cmt-canvas-popover")) return;
|
|
714
755
|
const marker = markerRef.current;
|
|
715
756
|
if (marker && marker.contains(target)) return;
|
|
716
|
-
if (target.closest(".cmt-canvas-region-handle, .cmt-canvas-region--movable")) return;
|
|
717
|
-
if (target.closest(".tlui-menu, [data-radix-popper-content-wrapper], .cmt-mention-popup"))
|
|
757
|
+
if (target.closest(".tlui-cmt-canvas-region-handle, .tlui-cmt-canvas-region--movable")) return;
|
|
758
|
+
if (target.closest(".tlui-menu, [data-radix-popper-content-wrapper], .tlui-cmt-mention-popup"))
|
|
718
759
|
return;
|
|
719
760
|
openThreadId.set(editor, null);
|
|
720
761
|
};
|
|
@@ -725,165 +766,68 @@ const ThreadPin = memo(function ThreadPin2({
|
|
|
725
766
|
"pin point",
|
|
726
767
|
() => {
|
|
727
768
|
if (thread.pageId !== editor.getCurrentPageId()) return null;
|
|
728
|
-
const pagePoint = anchorPagePoint(editor, thread.anchor
|
|
729
|
-
|
|
769
|
+
const pagePoint = anchorPagePoint(editor, thread.anchor);
|
|
770
|
+
if (!pagePoint) return null;
|
|
771
|
+
const viewportPoint = editor.pageToViewport(pagePoint);
|
|
772
|
+
const inset = impreciseShapePinInset(editor, thread.anchor);
|
|
773
|
+
return inset ? { x: viewportPoint.x + inset.x, y: viewportPoint.y + inset.y } : viewportPoint;
|
|
730
774
|
},
|
|
731
|
-
[editor, thread.anchor, thread.pageId
|
|
775
|
+
[editor, thread.anchor, thread.pageId]
|
|
732
776
|
);
|
|
733
|
-
const visible = point !== null;
|
|
734
|
-
useEffect(() => {
|
|
735
|
-
if (!open || !visible || !isCommentUnread || !onCommentRead) return;
|
|
736
|
-
for (const comment of comments) {
|
|
737
|
-
if (isCommentUnread(comment.id)) {
|
|
738
|
-
onCommentRead(comment.id);
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
}, [open, visible, comments, isCommentUnread, onCommentRead]);
|
|
742
777
|
if (!point) return null;
|
|
743
|
-
const postReply = () => {
|
|
744
|
-
if (isCommentEmpty(reply) || !currentUserId) return;
|
|
745
|
-
commitCommentMutation(editor, () => {
|
|
746
|
-
const comment = createComment({
|
|
747
|
-
threadId: thread.id,
|
|
748
|
-
pageId: thread.pageId,
|
|
749
|
-
authorId: currentUserId,
|
|
750
|
-
body: reply
|
|
751
|
-
});
|
|
752
|
-
putCommentRecords(editor, [comment]);
|
|
753
|
-
if (onPostComment) onPostComment(comment);
|
|
754
|
-
});
|
|
755
|
-
setReply(EMPTY_COMMENT);
|
|
756
|
-
};
|
|
757
|
-
const toggleResolve = () => {
|
|
758
|
-
if (!currentUserId) return;
|
|
759
|
-
commitCommentMutation(editor, () => {
|
|
760
|
-
putCommentRecords(editor, [
|
|
761
|
-
{
|
|
762
|
-
...thread,
|
|
763
|
-
resolved: thread.resolved ? null : { at: Date.now(), by: currentUserId }
|
|
764
|
-
}
|
|
765
|
-
]);
|
|
766
|
-
});
|
|
767
|
-
};
|
|
768
|
-
const deleteThread = () => {
|
|
769
|
-
openThreadId.set(editor, null);
|
|
770
|
-
commitCommentMutation(
|
|
771
|
-
editor,
|
|
772
|
-
() => removeCommentRecords(editor, [thread.id, ...comments.map((c) => c.id)])
|
|
773
|
-
);
|
|
774
|
-
};
|
|
775
|
-
const startEdit = (comment) => {
|
|
776
|
-
setEditingId(comment.id);
|
|
777
|
-
setEditText(comment.body);
|
|
778
|
-
};
|
|
779
|
-
const saveEdit = () => {
|
|
780
|
-
const comment = comments.find((c) => c.id === editingId);
|
|
781
|
-
if (!comment || isCommentEmpty(editText)) return;
|
|
782
|
-
commitCommentMutation(editor, () => {
|
|
783
|
-
putCommentRecords(editor, [{ ...comment, body: editText, editedAt: Date.now() }]);
|
|
784
|
-
});
|
|
785
|
-
setEditingId(null);
|
|
786
|
-
};
|
|
787
|
-
const renderComment = (card, index) => {
|
|
788
|
-
const comment = comments[index];
|
|
789
|
-
if (editingId === comment.id) {
|
|
790
|
-
return /* @__PURE__ */ jsx(
|
|
791
|
-
"div",
|
|
792
|
-
{
|
|
793
|
-
className: "cmt-editing",
|
|
794
|
-
onKeyDown: (e) => {
|
|
795
|
-
if (e.key === "Escape") {
|
|
796
|
-
setEditingId(null);
|
|
797
|
-
e.stopPropagation();
|
|
798
|
-
}
|
|
799
|
-
},
|
|
800
|
-
children: /* @__PURE__ */ jsx(
|
|
801
|
-
CommentComposer,
|
|
802
|
-
{
|
|
803
|
-
author: card.author,
|
|
804
|
-
placeholder: msg("comments.edit-placeholder"),
|
|
805
|
-
value: editText,
|
|
806
|
-
onChange: setEditText,
|
|
807
|
-
onSubmit: saveEdit,
|
|
808
|
-
sendLabel: msg("comments.save"),
|
|
809
|
-
disabled: isCommentEmpty(editText),
|
|
810
|
-
getMentionSuggestions,
|
|
811
|
-
renderMentionSuggestion,
|
|
812
|
-
autoFocus: true
|
|
813
|
-
}
|
|
814
|
-
)
|
|
815
|
-
}
|
|
816
|
-
);
|
|
817
|
-
}
|
|
818
|
-
return /* @__PURE__ */ jsx(
|
|
819
|
-
CommentCard,
|
|
820
|
-
{
|
|
821
|
-
...card,
|
|
822
|
-
actions: comment.authorId === currentUserId ? /* @__PURE__ */ jsx(
|
|
823
|
-
"button",
|
|
824
|
-
{
|
|
825
|
-
className: "cmt-thread__action",
|
|
826
|
-
title: msg("comments.edit"),
|
|
827
|
-
onClick: () => startEdit(comment),
|
|
828
|
-
children: /* @__PURE__ */ jsx(TldrawUiIcon, { icon: "dots-horizontal", label: msg("comments.edit"), small: true })
|
|
829
|
-
}
|
|
830
|
-
) : void 0
|
|
831
|
-
}
|
|
832
|
-
);
|
|
833
|
-
};
|
|
834
|
-
const headerActions = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
835
|
-
currentUserId && /* @__PURE__ */ jsx(
|
|
836
|
-
"button",
|
|
837
|
-
{
|
|
838
|
-
className: "cmt-thread__action",
|
|
839
|
-
title: msg(thread.resolved ? "comments.reopen" : "comments.resolve"),
|
|
840
|
-
onClick: toggleResolve,
|
|
841
|
-
children: /* @__PURE__ */ jsx(
|
|
842
|
-
TldrawUiIcon,
|
|
843
|
-
{
|
|
844
|
-
icon: "check",
|
|
845
|
-
label: msg(thread.resolved ? "comments.reopen" : "comments.resolve"),
|
|
846
|
-
small: true
|
|
847
|
-
}
|
|
848
|
-
)
|
|
849
|
-
}
|
|
850
|
-
),
|
|
851
|
-
currentUserId && /* @__PURE__ */ jsx(
|
|
852
|
-
"button",
|
|
853
|
-
{
|
|
854
|
-
className: "cmt-thread__action",
|
|
855
|
-
title: msg("comments.delete"),
|
|
856
|
-
onClick: deleteThread,
|
|
857
|
-
children: /* @__PURE__ */ jsx(TldrawUiIcon, { icon: "trash", label: msg("comments.delete"), small: true })
|
|
858
|
-
}
|
|
859
|
-
),
|
|
860
|
-
/* @__PURE__ */ jsx(
|
|
861
|
-
"button",
|
|
862
|
-
{
|
|
863
|
-
className: "cmt-thread__action",
|
|
864
|
-
title: msg("comments.dismiss"),
|
|
865
|
-
onClick: () => openThreadId.set(editor, null),
|
|
866
|
-
children: /* @__PURE__ */ jsx(TldrawUiIcon, { icon: "cross-2", label: msg("comments.dismiss"), small: true })
|
|
867
|
-
}
|
|
868
|
-
)
|
|
869
|
-
] });
|
|
870
778
|
const PinContent = options.components.PinContent;
|
|
871
|
-
const
|
|
779
|
+
const threadAuthor = resolveAuthor(thread.createdBy);
|
|
780
|
+
const pinContent = PinContent ? /* @__PURE__ */ jsx(PinContent, { thread, comments }) : /* @__PURE__ */ jsx(Avatar, { author: threadAuthor ?? UNKNOWN_COMMENT_AUTHOR });
|
|
781
|
+
const pinLabel = msg(
|
|
782
|
+
thread.resolved ? "comments.pin-label-resolved" : "comments.pin-label"
|
|
783
|
+
).replace("{name}", threadAuthor?.name ?? UNKNOWN_AUTHOR);
|
|
872
784
|
const isRegion = thread.anchor.type === "region";
|
|
873
|
-
const pinMovable = regionOptions.move !== "body";
|
|
874
|
-
const bodyMovable = regionOptions.move !== "pin";
|
|
875
785
|
const startDrag = (e) => {
|
|
786
|
+
if (isCanvasPanGesture(editor, e)) {
|
|
787
|
+
forwardPointerEventToCanvas(container, e);
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
876
790
|
e.stopPropagation();
|
|
877
|
-
|
|
791
|
+
const grabPage = editor.screenToPage({ x: e.clientX, y: e.clientY });
|
|
792
|
+
const anchorPage = anchorPagePoint(editor, thread.anchor);
|
|
793
|
+
const inset = impreciseShapePinInset(editor, thread.anchor);
|
|
794
|
+
if (anchorPage && inset) {
|
|
795
|
+
const zoom = editor.getZoomLevel();
|
|
796
|
+
anchorPage.x += inset.x / zoom;
|
|
797
|
+
anchorPage.y += inset.y / zoom;
|
|
798
|
+
}
|
|
799
|
+
dragRef.current = {
|
|
800
|
+
startX: e.clientX,
|
|
801
|
+
startY: e.clientY,
|
|
802
|
+
moved: false,
|
|
803
|
+
offsetX: anchorPage ? anchorPage.x - grabPage.x : 0,
|
|
804
|
+
offsetY: anchorPage ? anchorPage.y - grabPage.y : 0
|
|
805
|
+
};
|
|
878
806
|
e.currentTarget.setPointerCapture(e.pointerId);
|
|
879
807
|
};
|
|
880
808
|
const onDrag = (e) => {
|
|
881
809
|
const drag = dragRef.current;
|
|
882
810
|
if (!drag) return;
|
|
883
|
-
if (
|
|
811
|
+
if (!canComment) return;
|
|
884
812
|
if (!drag.moved && Math.hypot(e.clientX - drag.startX, e.clientY - drag.startY) < 4) return;
|
|
885
813
|
drag.moved = true;
|
|
886
|
-
|
|
814
|
+
const cursorPage = editor.screenToPage({ x: e.clientX, y: e.clientY });
|
|
815
|
+
const pagePoint = { x: cursorPage.x + drag.offsetX, y: cursorPage.y + drag.offsetY };
|
|
816
|
+
setDragPagePoint(pagePoint);
|
|
817
|
+
if (!isRegion) {
|
|
818
|
+
const hit = commentTargetShapeAt(editor, pagePoint);
|
|
819
|
+
editor.setHintingShapes(hit ? [hit.id] : []);
|
|
820
|
+
}
|
|
821
|
+
};
|
|
822
|
+
const cancelDrag = (e) => {
|
|
823
|
+
const drag = dragRef.current;
|
|
824
|
+
dragRef.current = null;
|
|
825
|
+
if (e.currentTarget.hasPointerCapture(e.pointerId)) {
|
|
826
|
+
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
827
|
+
}
|
|
828
|
+
if (!drag) return;
|
|
829
|
+
setDragPagePoint(null);
|
|
830
|
+
editor.setHintingShapes([]);
|
|
887
831
|
};
|
|
888
832
|
const endDrag = (e) => {
|
|
889
833
|
const drag = dragRef.current;
|
|
@@ -892,55 +836,59 @@ const ThreadPin = memo(function ThreadPin2({
|
|
|
892
836
|
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
893
837
|
}
|
|
894
838
|
if (!drag) return;
|
|
839
|
+
editor.setHintingShapes([]);
|
|
895
840
|
if (!drag.moved) {
|
|
896
841
|
openThreadId.set(editor, openThreadId.get(editor) === thread.id ? null : thread.id);
|
|
897
842
|
return;
|
|
898
843
|
}
|
|
899
|
-
const
|
|
844
|
+
const cursorPage = editor.screenToPage({ x: e.clientX, y: e.clientY });
|
|
845
|
+
const pagePoint = { x: cursorPage.x + drag.offsetX, y: cursorPage.y + drag.offsetY };
|
|
900
846
|
setDragPagePoint(null);
|
|
901
847
|
let anchor;
|
|
902
848
|
if (thread.anchor.type === "region") {
|
|
903
849
|
anchor = {
|
|
904
850
|
...thread.anchor,
|
|
905
|
-
x: pagePoint.x -
|
|
906
|
-
y: pagePoint.y -
|
|
851
|
+
x: pagePoint.x - pinCorner.x * thread.anchor.w,
|
|
852
|
+
y: pagePoint.y - pinCorner.y * thread.anchor.h
|
|
907
853
|
};
|
|
908
854
|
} else {
|
|
909
|
-
const hit = editor
|
|
910
|
-
anchor = hit ? shapeAnchorAt(
|
|
855
|
+
const hit = commentTargetShapeAt(editor, pagePoint);
|
|
856
|
+
anchor = hit ? shapeAnchorAt(
|
|
857
|
+
editor,
|
|
858
|
+
hit.id,
|
|
859
|
+
pagePoint,
|
|
860
|
+
getCommentingOptions(editor).shouldBePrecise(editor, {
|
|
861
|
+
shapeId: hit.id,
|
|
862
|
+
point: pagePoint,
|
|
863
|
+
altKey: e.altKey
|
|
864
|
+
})
|
|
865
|
+
) : { type: "point", x: pagePoint.x, y: pagePoint.y };
|
|
911
866
|
}
|
|
912
|
-
commitCommentMutation(editor, () =>
|
|
867
|
+
commitCommentMutation(editor, () => putRecordsInCommit(editor, [{ ...thread, anchor }]), "drag");
|
|
913
868
|
};
|
|
914
|
-
const livePinPage = resizeBounds ? regionPinPoint(resizeBounds,
|
|
915
|
-
const
|
|
869
|
+
const livePinPage = resizeBounds ? regionPinPoint(resizeBounds, pinCorner) : dragPagePoint;
|
|
870
|
+
const renderPointBase = livePinPage ? editor.pageToViewport(livePinPage) : point;
|
|
871
|
+
const renderPoint = isRegion ? { x: renderPointBase.x - 17, y: renderPointBase.y + 17 } : renderPointBase;
|
|
916
872
|
const regionAnchor = thread.anchor.type === "region" ? thread.anchor : void 0;
|
|
917
873
|
const movedRegion = regionAnchor && dragPagePoint ? {
|
|
918
874
|
...regionAnchor,
|
|
919
|
-
x: dragPagePoint.x -
|
|
920
|
-
y: dragPagePoint.y -
|
|
875
|
+
x: dragPagePoint.x - pinCorner.x * regionAnchor.w,
|
|
876
|
+
y: dragPagePoint.y - pinCorner.y * regionAnchor.h
|
|
921
877
|
} : regionAnchor;
|
|
922
878
|
const regionBoxBounds = resizeBounds ?? movedRegion;
|
|
923
879
|
const commitResize = (bounds) => {
|
|
924
880
|
setResizeBounds(null);
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
}
|
|
881
|
+
if (!canComment) return;
|
|
882
|
+
commitCommentMutation(
|
|
883
|
+
editor,
|
|
884
|
+
// Spread the existing anchor first so the region's pin corner survives a resize.
|
|
885
|
+
() => putRecordsInCommit(editor, [{ ...thread, anchor: { ...regionAnchor, ...bounds } }]),
|
|
886
|
+
"drag"
|
|
930
887
|
);
|
|
931
888
|
};
|
|
932
889
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
933
|
-
regionBoxBounds && (dragPagePoint || revealed) && /* @__PURE__ */ jsx(
|
|
934
|
-
|
|
935
|
-
{
|
|
936
|
-
editor,
|
|
937
|
-
box: regionBoxBounds,
|
|
938
|
-
movable: bodyMovable && !dragPagePoint,
|
|
939
|
-
onPreview: setResizeBounds,
|
|
940
|
-
onCommit: commitResize
|
|
941
|
-
}
|
|
942
|
-
),
|
|
943
|
-
regionBoxBounds && revealed && !dragPagePoint && regionOptions.resize !== "none" && /* @__PURE__ */ jsx(
|
|
890
|
+
regionBoxBounds && (dragPagePoint || revealed) && /* @__PURE__ */ jsx(RegionBox, { editor, box: regionBoxBounds }),
|
|
891
|
+
regionBoxBounds && revealed && !dragPagePoint && canComment && /* @__PURE__ */ jsx(
|
|
944
892
|
RegionResizeHandles,
|
|
945
893
|
{
|
|
946
894
|
editor,
|
|
@@ -953,19 +901,33 @@ const ThreadPin = memo(function ThreadPin2({
|
|
|
953
901
|
/* @__PURE__ */ jsxs(
|
|
954
902
|
"div",
|
|
955
903
|
{
|
|
956
|
-
className:
|
|
904
|
+
className: [
|
|
905
|
+
"tlui-cmt-canvas-pin",
|
|
906
|
+
open && "tlui-cmt-canvas-pin--open",
|
|
907
|
+
dragPagePoint && "tlui-cmt-canvas-pin--dragging"
|
|
908
|
+
].filter(Boolean).join(" "),
|
|
957
909
|
style: { left: renderPoint.x, top: renderPoint.y },
|
|
958
910
|
children: [
|
|
959
911
|
/* @__PURE__ */ jsx(
|
|
960
|
-
"
|
|
912
|
+
"button",
|
|
961
913
|
{
|
|
962
914
|
ref: markerRef,
|
|
963
|
-
|
|
915
|
+
type: "button",
|
|
916
|
+
className: "tlui-cmt-button tlui-cmt-canvas-pin__marker",
|
|
917
|
+
"aria-label": pinLabel,
|
|
918
|
+
"aria-expanded": open,
|
|
964
919
|
onPointerDown: startDrag,
|
|
965
920
|
onPointerMove: onDrag,
|
|
966
921
|
onPointerUp: endDrag,
|
|
967
|
-
|
|
968
|
-
|
|
922
|
+
onPointerCancel: cancelDrag,
|
|
923
|
+
onClick: (e) => {
|
|
924
|
+
if (e.detail !== 0) return;
|
|
925
|
+
openThreadId.set(editor, openThreadId.get(editor) === thread.id ? null : thread.id);
|
|
926
|
+
},
|
|
927
|
+
onPointerEnter: previewHandlers.onPointerEnter,
|
|
928
|
+
onPointerLeave: previewHandlers.onPointerLeave,
|
|
929
|
+
onFocus: previewHandlers.onPointerEnter,
|
|
930
|
+
onBlur: previewHandlers.onPointerLeave,
|
|
969
931
|
children: /* @__PURE__ */ jsx(CommentPin, { resolved: thread.resolved != null, open, children: pinContent })
|
|
970
932
|
}
|
|
971
933
|
),
|
|
@@ -973,31 +935,25 @@ const ThreadPin = memo(function ThreadPin2({
|
|
|
973
935
|
ThreadPopover,
|
|
974
936
|
{
|
|
975
937
|
container,
|
|
976
|
-
style: {
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
disabled: isCommentEmpty(reply),
|
|
996
|
-
getMentionSuggestions,
|
|
997
|
-
renderMentionSuggestion
|
|
998
|
-
} : void 0
|
|
999
|
-
}
|
|
1000
|
-
)
|
|
938
|
+
style: {
|
|
939
|
+
left: renderPoint.x + POPOVER_OFFSET.thread.x,
|
|
940
|
+
top: renderPoint.y + POPOVER_OFFSET.thread.y - THREAD_HEADER_BLOCK
|
|
941
|
+
},
|
|
942
|
+
children: /* @__PURE__ */ jsx(ThreadView, { editor, thread, ...props })
|
|
943
|
+
}
|
|
944
|
+
),
|
|
945
|
+
previewShown && !dragPagePoint && /* @__PURE__ */ jsx(
|
|
946
|
+
ThreadPreview,
|
|
947
|
+
{
|
|
948
|
+
editor,
|
|
949
|
+
threads: previewThreads,
|
|
950
|
+
container,
|
|
951
|
+
variant: "thread",
|
|
952
|
+
point: renderPoint,
|
|
953
|
+
onSelectThread: () => openThreadId.set(editor, thread.id),
|
|
954
|
+
...previewHandlers,
|
|
955
|
+
currentUserId: props.currentUserId,
|
|
956
|
+
resolveAuthor
|
|
1001
957
|
}
|
|
1002
958
|
)
|
|
1003
959
|
]
|
|
@@ -1009,12 +965,18 @@ function PendingComposer({
|
|
|
1009
965
|
editor,
|
|
1010
966
|
pending,
|
|
1011
967
|
currentUserId,
|
|
1012
|
-
|
|
968
|
+
resolveAuthor,
|
|
1013
969
|
onPostComment,
|
|
1014
970
|
getMentionSuggestions,
|
|
1015
971
|
renderMentionSuggestion
|
|
1016
972
|
}) {
|
|
1017
|
-
const
|
|
973
|
+
const ComposerFallback = useCommentingOptions().components.ComposerFallback;
|
|
974
|
+
const canComment = useCanComment(currentUserId);
|
|
975
|
+
const me = currentUserId ? resolveAuthor(currentUserId) : void 0;
|
|
976
|
+
const draftAvatar = /* @__PURE__ */ jsx(CommentPin, { children: /* @__PURE__ */ jsx(Avatar, { author: me ?? UNKNOWN_COMMENT_AUTHOR }) });
|
|
977
|
+
const [text, setText] = useState(
|
|
978
|
+
() => getCommentDraft(NEW_COMMENT_DRAFT) ?? EMPTY_COMMENT
|
|
979
|
+
);
|
|
1018
980
|
const ref = useRef(null);
|
|
1019
981
|
const msg = useTranslation();
|
|
1020
982
|
const container = useContainer();
|
|
@@ -1029,7 +991,7 @@ function PendingComposer({
|
|
|
1029
991
|
const el = ref.current;
|
|
1030
992
|
const target = e.target;
|
|
1031
993
|
if (!el || !target) return;
|
|
1032
|
-
if (el.contains(target) || target.closest(".cmt-mention-popup")) return;
|
|
994
|
+
if (el.contains(target) || target.closest(".tlui-cmt-mention-popup")) return;
|
|
1033
995
|
pendingComment.set(editor, null);
|
|
1034
996
|
};
|
|
1035
997
|
document.addEventListener("pointerdown", onPointerDown, true);
|
|
@@ -1050,10 +1012,11 @@ function PendingComposer({
|
|
|
1050
1012
|
authorId: currentUserId,
|
|
1051
1013
|
body: text
|
|
1052
1014
|
});
|
|
1053
|
-
|
|
1015
|
+
putRecordsInCommit(editor, [thread, comment]);
|
|
1054
1016
|
if (onPostComment) onPostComment(comment);
|
|
1055
1017
|
});
|
|
1056
1018
|
setText(EMPTY_COMMENT);
|
|
1019
|
+
clearCommentDraft(NEW_COMMENT_DRAFT);
|
|
1057
1020
|
pendingComment.set(editor, null);
|
|
1058
1021
|
};
|
|
1059
1022
|
return createPortal(
|
|
@@ -1061,28 +1024,36 @@ function PendingComposer({
|
|
|
1061
1024
|
"div",
|
|
1062
1025
|
{
|
|
1063
1026
|
ref,
|
|
1064
|
-
className:
|
|
1027
|
+
className: [
|
|
1028
|
+
"tlui-cmt-canvas-composer",
|
|
1029
|
+
pending.anchor.type === "region" && "tlui-cmt-canvas-composer--region",
|
|
1030
|
+
!canComment && "tlui-cmt-canvas-composer--fallback"
|
|
1031
|
+
].filter(Boolean).join(" "),
|
|
1065
1032
|
style: { left: point.x, top: point.y },
|
|
1066
1033
|
onPointerDown: stop,
|
|
1034
|
+
onContextMenu: stop,
|
|
1067
1035
|
onKeyDown: (e) => {
|
|
1068
1036
|
if (e.key === "Escape" && !isMentionPickerOpen()) pendingComment.set(editor, null);
|
|
1069
1037
|
},
|
|
1070
|
-
children: /* @__PURE__ */ jsx(
|
|
1038
|
+
children: canComment ? /* @__PURE__ */ jsx(
|
|
1071
1039
|
CommentComposer,
|
|
1072
1040
|
{
|
|
1073
|
-
author:
|
|
1041
|
+
author: me ?? UNKNOWN_COMMENT_AUTHOR,
|
|
1074
1042
|
placeholder: msg("comments.add-placeholder"),
|
|
1075
1043
|
sendLabel: msg("comments.send"),
|
|
1076
1044
|
value: text,
|
|
1077
|
-
onChange:
|
|
1045
|
+
onChange: (value) => {
|
|
1046
|
+
setText(value);
|
|
1047
|
+
saveCommentDraft(NEW_COMMENT_DRAFT, value);
|
|
1048
|
+
},
|
|
1078
1049
|
onSubmit: submit,
|
|
1079
|
-
disabled: isCommentEmpty(text),
|
|
1050
|
+
disabled: isCommentEmpty(text) || !currentUserId,
|
|
1080
1051
|
getMentionSuggestions,
|
|
1081
1052
|
renderMentionSuggestion,
|
|
1082
1053
|
autoFocus: true,
|
|
1083
1054
|
leading: draftAvatar
|
|
1084
1055
|
}
|
|
1085
|
-
)
|
|
1056
|
+
) : ComposerFallback && /* @__PURE__ */ jsx(ComposerFallback, { context: "pending" })
|
|
1086
1057
|
}
|
|
1087
1058
|
),
|
|
1088
1059
|
container
|