@tldraw/commenting 0.0.0-bootstrap → 5.3.0-internal.1640468db8fd
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/commenting.css +284 -153
- 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-render.js +2 -0
- package/dist-cjs/canvas/comment-render.js.map +2 -2
- package/dist-cjs/canvas/comment-tool.js +65 -12
- package/dist-cjs/canvas/comment-tool.js.map +2 -2
- package/dist-cjs/canvas/comments-filter-menu.js +54 -63
- package/dist-cjs/canvas/comments-filter-menu.js.map +2 -2
- package/dist-cjs/canvas/comments-overflow-menu.js +21 -27
- package/dist-cjs/canvas/comments-overflow-menu.js.map +2 -2
- package/dist-cjs/canvas/comments-overlay.js +304 -112
- package/dist-cjs/canvas/comments-overlay.js.map +2 -2
- package/dist-cjs/canvas/comments-sidebar.js +12 -18
- package/dist-cjs/canvas/comments-sidebar.js.map +2 -2
- package/dist-cjs/canvas/comments-visibility-toggle.js +66 -0
- package/dist-cjs/canvas/comments-visibility-toggle.js.map +7 -0
- package/dist-cjs/canvas/options.js +1 -0
- package/dist-cjs/canvas/options.js.map +2 -2
- 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 +11 -0
- package/dist-cjs/canvas/state.js.map +2 -2
- package/dist-cjs/canvas/thread-state.js +8 -1
- package/dist-cjs/canvas/thread-state.js.map +2 -2
- package/dist-cjs/clustering/runtime.js +119 -7
- package/dist-cjs/clustering/runtime.js.map +2 -2
- package/dist-cjs/index.d.ts +126 -41
- package/dist-cjs/index.js +7 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/ui/avatar.js +14 -6
- package/dist-cjs/ui/avatar.js.map +2 -2
- package/dist-cjs/ui/byline.js +4 -4
- package/dist-cjs/ui/byline.js.map +2 -2
- package/dist-cjs/ui/comment-author.js +17 -0
- package/dist-cjs/ui/comment-author.js.map +7 -0
- package/dist-cjs/ui/comment-card.js +4 -4
- package/dist-cjs/ui/comment-card.js.map +2 -2
- package/dist-cjs/ui/comment-composer.js +92 -29
- package/dist-cjs/ui/comment-composer.js.map +2 -2
- package/dist-cjs/ui/comment-mention.js +1 -1
- package/dist-cjs/ui/comment-mention.js.map +2 -2
- package/dist-cjs/ui/comment-pin.js +23 -3
- package/dist-cjs/ui/comment-pin.js.map +2 -2
- package/dist-cjs/ui/comment-text.js +1 -1
- package/dist-cjs/ui/comment-text.js.map +2 -2
- package/dist-cjs/ui/comment-thread.js +6 -6
- package/dist-cjs/ui/comment-thread.js.map +2 -2
- package/dist-cjs/ui/comments-list.js +12 -12
- package/dist-cjs/ui/comments-list.js.map +2 -2
- package/dist-cjs/ui/count-badge.js +1 -1
- package/dist-cjs/ui/count-badge.js.map +2 -2
- 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 +3 -0
- package/dist-cjs/ui/format-time.js.map +2 -2
- package/dist-cjs/ui/mention-list.js +8 -8
- package/dist-cjs/ui/mention-list.js.map +2 -2
- package/dist-cjs/ui/mention-suggestion.js +3 -3
- package/dist-cjs/ui/mention-suggestion.js.map +2 -2
- package/dist-cjs/ui/mention.js +1 -1
- package/dist-cjs/ui/mention.js.map +2 -2
- package/dist-cjs/ui/reaction.js +11 -4
- package/dist-cjs/ui/reaction.js.map +2 -2
- package/dist-cjs/ui/reactions.js +10 -2
- package/dist-cjs/ui/reactions.js.map +2 -2
- package/dist-cjs/ui/send-button.js +1 -1
- package/dist-cjs/ui/send-button.js.map +2 -2
- package/dist-cjs/ui/tooltip-button.js +32 -0
- package/dist-cjs/ui/tooltip-button.js.map +7 -0
- 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-render.mjs +2 -0
- package/dist-esm/canvas/comment-render.mjs.map +2 -2
- package/dist-esm/canvas/comment-tool.mjs +67 -14
- package/dist-esm/canvas/comment-tool.mjs.map +2 -2
- package/dist-esm/canvas/comments-filter-menu.mjs +54 -63
- package/dist-esm/canvas/comments-filter-menu.mjs.map +2 -2
- package/dist-esm/canvas/comments-overflow-menu.mjs +22 -27
- package/dist-esm/canvas/comments-overflow-menu.mjs.map +2 -2
- package/dist-esm/canvas/comments-overlay.mjs +322 -114
- package/dist-esm/canvas/comments-overlay.mjs.map +2 -2
- package/dist-esm/canvas/comments-sidebar.mjs +15 -21
- package/dist-esm/canvas/comments-sidebar.mjs.map +2 -2
- package/dist-esm/canvas/comments-visibility-toggle.mjs +46 -0
- package/dist-esm/canvas/comments-visibility-toggle.mjs.map +7 -0
- package/dist-esm/canvas/options.mjs +1 -0
- package/dist-esm/canvas/options.mjs.map +2 -2
- 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 +11 -0
- package/dist-esm/canvas/state.mjs.map +2 -2
- package/dist-esm/canvas/thread-state.mjs +8 -1
- package/dist-esm/canvas/thread-state.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 +126 -41
- package/dist-esm/index.mjs +11 -1
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/ui/avatar.mjs +14 -6
- package/dist-esm/ui/avatar.mjs.map +2 -2
- package/dist-esm/ui/byline.mjs +4 -4
- package/dist-esm/ui/byline.mjs.map +2 -2
- package/dist-esm/ui/comment-author.mjs +1 -0
- package/dist-esm/ui/comment-author.mjs.map +7 -0
- package/dist-esm/ui/comment-card.mjs +4 -4
- package/dist-esm/ui/comment-card.mjs.map +2 -2
- package/dist-esm/ui/comment-composer.mjs +93 -30
- package/dist-esm/ui/comment-composer.mjs.map +2 -2
- package/dist-esm/ui/comment-mention.mjs +1 -1
- package/dist-esm/ui/comment-mention.mjs.map +2 -2
- package/dist-esm/ui/comment-pin.mjs +23 -3
- package/dist-esm/ui/comment-pin.mjs.map +2 -2
- package/dist-esm/ui/comment-text.mjs +1 -1
- package/dist-esm/ui/comment-text.mjs.map +2 -2
- package/dist-esm/ui/comment-thread.mjs +6 -6
- package/dist-esm/ui/comment-thread.mjs.map +2 -2
- package/dist-esm/ui/comments-list.mjs +12 -12
- package/dist-esm/ui/comments-list.mjs.map +2 -2
- package/dist-esm/ui/count-badge.mjs +1 -1
- package/dist-esm/ui/count-badge.mjs.map +2 -2
- 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 +3 -0
- package/dist-esm/ui/format-time.mjs.map +2 -2
- package/dist-esm/ui/mention-list.mjs +8 -8
- package/dist-esm/ui/mention-list.mjs.map +2 -2
- package/dist-esm/ui/mention-suggestion.mjs +3 -3
- package/dist-esm/ui/mention-suggestion.mjs.map +2 -2
- package/dist-esm/ui/mention.mjs +1 -1
- package/dist-esm/ui/mention.mjs.map +2 -2
- package/dist-esm/ui/reaction.mjs +11 -4
- package/dist-esm/ui/reaction.mjs.map +2 -2
- package/dist-esm/ui/reactions.mjs +10 -2
- package/dist-esm/ui/reactions.mjs.map +2 -2
- package/dist-esm/ui/send-button.mjs +1 -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 +3 -3
- package/src/canvas/canvas.css +79 -36
- 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-render.test.ts +1 -1
- package/src/canvas/comment-render.ts +7 -2
- package/src/canvas/comment-tool.tsx +98 -17
- package/src/canvas/comments-filter-menu.tsx +27 -33
- package/src/canvas/comments-overflow-menu.tsx +13 -21
- package/src/canvas/comments-overlay.tsx +441 -145
- package/src/canvas/comments-sidebar.tsx +27 -28
- package/src/canvas/comments-visibility-toggle.tsx +50 -0
- package/src/canvas/options.ts +25 -0
- package/src/canvas/sidebar-filters.ts +4 -3
- package/src/canvas/state.ts +22 -0
- package/src/canvas/thread-state.ts +16 -3
- package/src/clustering/runtime.test.ts +183 -7
- package/src/clustering/runtime.ts +164 -15
- package/src/index.ts +7 -0
- package/src/ui/avatar.tsx +15 -11
- package/src/ui/byline.tsx +6 -5
- package/src/ui/comment-author.ts +15 -0
- package/src/ui/comment-card.tsx +6 -5
- package/src/ui/comment-composer.tsx +122 -33
- package/src/ui/comment-mention.ts +2 -2
- package/src/ui/comment-pin.tsx +35 -4
- package/src/ui/comment-text.tsx +1 -1
- package/src/ui/comment-thread.tsx +8 -6
- package/src/ui/comments-list.tsx +19 -14
- package/src/ui/comments.css +205 -117
- package/src/ui/count-badge.tsx +1 -1
- package/src/ui/empty-state.tsx +3 -3
- package/src/ui/format-time.ts +5 -0
- package/src/ui/mention-list.tsx +13 -16
- package/src/ui/mention-suggestion.tsx +3 -3
- package/src/ui/mention.tsx +1 -1
- package/src/ui/reaction.tsx +6 -3
- package/src/ui/reactions.tsx +6 -2
- package/src/ui/send-button.tsx +1 -1
- package/src/ui/tooltip-button.tsx +20 -0
|
@@ -13,6 +13,11 @@ import {
|
|
|
13
13
|
createCommentThread,
|
|
14
14
|
getFirstCharacter,
|
|
15
15
|
react,
|
|
16
|
+
TldrawUiDropdownMenuContent,
|
|
17
|
+
TldrawUiDropdownMenuGroup,
|
|
18
|
+
TldrawUiDropdownMenuItem,
|
|
19
|
+
TldrawUiDropdownMenuRoot,
|
|
20
|
+
TldrawUiDropdownMenuTrigger,
|
|
16
21
|
TldrawUiIcon,
|
|
17
22
|
useContainer,
|
|
18
23
|
useEditor,
|
|
@@ -30,9 +35,17 @@ import { CommentPin } from "../ui/comment-pin.mjs";
|
|
|
30
35
|
import { CommentThread } from "../ui/comment-thread.mjs";
|
|
31
36
|
import { CountBadge } from "../ui/count-badge.mjs";
|
|
32
37
|
import { isMentionPickerOpen } from "../ui/mention-suggestion.mjs";
|
|
38
|
+
import { TooltipButton } from "../ui/tooltip-button.mjs";
|
|
33
39
|
import { collectClusterLeaves } from "./cluster-input.mjs";
|
|
34
40
|
import { CommentBody } from "./comment-body.mjs";
|
|
35
|
-
import {
|
|
41
|
+
import {
|
|
42
|
+
clearCommentDraft,
|
|
43
|
+
getCommentDraft,
|
|
44
|
+
NEW_COMMENT_DRAFT,
|
|
45
|
+
replyDraftSlot,
|
|
46
|
+
saveCommentDraft
|
|
47
|
+
} from "./comment-drafts.mjs";
|
|
48
|
+
import { UNKNOWN_AUTHOR, UNKNOWN_COMMENT_AUTHOR } from "./comment-render.mjs";
|
|
36
49
|
import { getCommentRecord, putCommentRecords, removeCommentRecords } from "./comment-store.mjs";
|
|
37
50
|
import { useCommentThreads, useThreadComments } from "./hooks.mjs";
|
|
38
51
|
import { useCommentingEnabled } from "./license.mjs";
|
|
@@ -53,12 +66,33 @@ import {
|
|
|
53
66
|
toggleCommentsHidden,
|
|
54
67
|
usePendingComment
|
|
55
68
|
} from "./state.mjs";
|
|
56
|
-
import {
|
|
69
|
+
import {
|
|
70
|
+
anchorPagePoint,
|
|
71
|
+
regionAnchorPinCorner,
|
|
72
|
+
regionPinPoint,
|
|
73
|
+
shapeAnchorAt
|
|
74
|
+
} from "./thread-state.mjs";
|
|
57
75
|
const stop = (e) => e.stopPropagation();
|
|
76
|
+
const IMPRECISE_PIN_INSET_PX = 20;
|
|
77
|
+
function impreciseShapePinInset(anchor, spot) {
|
|
78
|
+
if (anchor.type !== "shape" || anchor.isPrecise) return null;
|
|
79
|
+
return {
|
|
80
|
+
x: Math.sign(0.5 - spot.x) * IMPRECISE_PIN_INSET_PX,
|
|
81
|
+
y: Math.sign(0.5 - spot.y) * IMPRECISE_PIN_INSET_PX
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const isCanvasPanGesture = (editor, e) => e.button !== 0 || editor.inputs.keys.has("Space");
|
|
85
|
+
function forwardPointerEventToCanvas(container, e) {
|
|
86
|
+
const cvs = container.querySelector(".tl-canvas");
|
|
87
|
+
if (!cvs) return;
|
|
88
|
+
const newEvent = new PointerEvent(e.type, e.nativeEvent);
|
|
89
|
+
newEvent.isSpecialRedispatchedEvent = true;
|
|
90
|
+
cvs.dispatchEvent(newEvent);
|
|
91
|
+
}
|
|
58
92
|
const initialOf = (name) => (getFirstCharacter(name.trim()) || "?").toUpperCase();
|
|
59
93
|
const CLUSTER_FADE_MS = 150;
|
|
60
94
|
const CLUSTER_EXPAND_ZOOM_MS = 450;
|
|
61
|
-
const draftAvatar = /* @__PURE__ */ jsx(CommentPin, { children: /* @__PURE__ */ jsxs(
|
|
95
|
+
const draftAvatar = (color) => /* @__PURE__ */ jsx(CommentPin, { color, children: /* @__PURE__ */ jsxs(
|
|
62
96
|
"svg",
|
|
63
97
|
{
|
|
64
98
|
viewBox: "0 0 24 24",
|
|
@@ -76,11 +110,11 @@ const draftAvatar = /* @__PURE__ */ jsx(CommentPin, { children: /* @__PURE__ */
|
|
|
76
110
|
]
|
|
77
111
|
}
|
|
78
112
|
) });
|
|
79
|
-
function toCardProps(comment, props, components) {
|
|
113
|
+
function toCardProps(comment, props, components, resolveName) {
|
|
80
114
|
const Body = components.CommentBody;
|
|
81
|
-
const body = Body ? /* @__PURE__ */ jsx(Body, { comment }) : /* @__PURE__ */ jsx(CommentBody, { richText: comment.body, resolveName
|
|
115
|
+
const body = Body ? /* @__PURE__ */ jsx(Body, { comment }) : /* @__PURE__ */ jsx(CommentBody, { richText: comment.body, resolveName });
|
|
82
116
|
return {
|
|
83
|
-
author: props.
|
|
117
|
+
author: props.resolveAuthor(comment.authorId) ?? UNKNOWN_COMMENT_AUTHOR,
|
|
84
118
|
body,
|
|
85
119
|
date: new Date(comment.createdAt).toISOString(),
|
|
86
120
|
you: comment.authorId === props.currentUserId,
|
|
@@ -102,24 +136,23 @@ function CanvasCommentsLayer(props) {
|
|
|
102
136
|
);
|
|
103
137
|
useEffect(() => setRegionCommentOptions(editor, regionOptions), [editor, regionOptions]);
|
|
104
138
|
const layerRef = useRef(null);
|
|
105
|
-
usePassThroughWheelEvents(layerRef);
|
|
106
139
|
usePassThroughMouseOverEvents(layerRef);
|
|
107
140
|
const deepLinkHandled = useRef(false);
|
|
108
141
|
const threads = useCommentThreads(editor);
|
|
109
142
|
const pending = usePendingComment();
|
|
110
143
|
const openId = useValue("open thread id", () => openThreadId.get(editor), [editor]);
|
|
111
144
|
const impreciseShapeAnchor = props.impreciseShapeAnchor ?? options.impreciseShapeAnchor;
|
|
112
|
-
const [
|
|
145
|
+
const [heldThreadIds, setHeldThreadIds] = useState(EMPTY_SET);
|
|
113
146
|
const adoptOnRebuild = useRef(false);
|
|
114
147
|
const clusterLeaves = useValue(
|
|
115
148
|
"comment cluster leaves",
|
|
116
149
|
() => collectClusterLeaves(
|
|
117
150
|
editor,
|
|
118
|
-
threads.filter((thread) => !
|
|
151
|
+
threads.filter((thread) => !heldThreadIds.has(thread.id)),
|
|
119
152
|
openThreadId.get(editor),
|
|
120
153
|
impreciseShapeAnchor
|
|
121
154
|
),
|
|
122
|
-
[editor, threads, impreciseShapeAnchor,
|
|
155
|
+
[editor, threads, impreciseShapeAnchor, heldThreadIds]
|
|
123
156
|
);
|
|
124
157
|
const clusterZoomBounds = useValue(
|
|
125
158
|
"comment cluster zoom bounds",
|
|
@@ -134,21 +167,41 @@ function CanvasCommentsLayer(props) {
|
|
|
134
167
|
}, [clusterLeaves, clusterZoomBounds, editor]);
|
|
135
168
|
const [renderedModel, setRenderedModel] = useState(latestModel);
|
|
136
169
|
let clusterModel = renderedModel;
|
|
137
|
-
|
|
170
|
+
const pageId = useValue("comment cluster page", () => editor.getCurrentPageId(), [editor]);
|
|
171
|
+
const pageRef = useRef(pageId);
|
|
172
|
+
if (pageRef.current !== pageId) {
|
|
173
|
+
pageRef.current = pageId;
|
|
174
|
+
adoptOnRebuild.current = false;
|
|
175
|
+
latestModel.runtime.seed(editor.getZoomLevel());
|
|
176
|
+
if (heldThreadIds.size > 0) setHeldThreadIds(EMPTY_SET);
|
|
177
|
+
setRenderedModel(latestModel);
|
|
178
|
+
clusterModel = latestModel;
|
|
179
|
+
}
|
|
180
|
+
const rejoinPending = heldThreadIds.size === 0 && adoptOnRebuild.current;
|
|
181
|
+
if (renderedModel !== latestModel && rejoinPending) {
|
|
138
182
|
adoptOnRebuild.current = false;
|
|
139
183
|
latestModel.runtime.seedFrom(editor.getZoomLevel(), renderedModel.runtime.getVisible());
|
|
140
184
|
setRenderedModel(latestModel);
|
|
141
185
|
clusterModel = latestModel;
|
|
186
|
+
} else if (heldThreadIds.size === 0 && renderedModel === latestModel) {
|
|
187
|
+
adoptOnRebuild.current = false;
|
|
142
188
|
}
|
|
143
189
|
const newlyMovedIds = findMovedClusteredLeafIds(clusterModel, latestModel);
|
|
144
190
|
if (newlyMovedIds.length > 0) {
|
|
145
|
-
|
|
146
|
-
const next = new Set(movedThreadIds);
|
|
191
|
+
const next = new Set(heldThreadIds);
|
|
147
192
|
for (const id of newlyMovedIds) next.add(id);
|
|
148
|
-
|
|
193
|
+
setHeldThreadIds(next);
|
|
194
|
+
}
|
|
195
|
+
{
|
|
196
|
+
const latestLeafIds = new Set(latestModel.table.leaves.map((leaf) => leaf.id));
|
|
197
|
+
for (const leaf of clusterModel.table.leaves) {
|
|
198
|
+
if (!latestLeafIds.has(leaf.id)) {
|
|
199
|
+
clusterModel.runtime.detachLeaf(leaf.id);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
149
202
|
}
|
|
150
203
|
useEffect(() => {
|
|
151
|
-
if (
|
|
204
|
+
if (heldThreadIds.size === 0) return;
|
|
152
205
|
let lastZoom = editor.getZoomLevel();
|
|
153
206
|
return react("rejoin moved comment pins on zoom out", () => {
|
|
154
207
|
const zoom = editor.getZoomLevel();
|
|
@@ -156,9 +209,9 @@ function CanvasCommentsLayer(props) {
|
|
|
156
209
|
lastZoom = zoom;
|
|
157
210
|
if (zoom >= prevZoom) return;
|
|
158
211
|
adoptOnRebuild.current = true;
|
|
159
|
-
|
|
212
|
+
setHeldThreadIds(EMPTY_SET);
|
|
160
213
|
});
|
|
161
|
-
}, [
|
|
214
|
+
}, [heldThreadIds, editor]);
|
|
162
215
|
useEffect(() => {
|
|
163
216
|
if (clusterModel === latestModel) return;
|
|
164
217
|
let lastZoom = editor.getZoomLevel();
|
|
@@ -171,31 +224,31 @@ function CanvasCommentsLayer(props) {
|
|
|
171
224
|
setRenderedModel(latestModel);
|
|
172
225
|
});
|
|
173
226
|
}, [clusterModel, latestModel, editor]);
|
|
227
|
+
const partitionVersion = clusterModel.runtime.version;
|
|
174
228
|
const orphanThreads = useMemo(() => {
|
|
175
229
|
if (clusterModel === latestModel) return [];
|
|
176
|
-
const
|
|
230
|
+
const displayed = /* @__PURE__ */ new Set();
|
|
231
|
+
for (const node of clusterModel.runtime.getVisible().values()) {
|
|
232
|
+
for (const member of node.members) displayed.add(member);
|
|
233
|
+
}
|
|
177
234
|
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,
|
|
235
|
+
return threads.filter((thread) => latestIds.has(thread.id) && !displayed.has(thread.id));
|
|
236
|
+
}, [clusterModel, latestModel, threads, partitionVersion]);
|
|
237
|
+
const heldThreads = useMemo(
|
|
238
|
+
() => threads.filter((thread) => heldThreadIds.has(thread.id) && thread.id !== openId),
|
|
239
|
+
[threads, heldThreadIds, openId]
|
|
183
240
|
);
|
|
184
|
-
|
|
185
|
-
"comment cluster
|
|
241
|
+
useValue(
|
|
242
|
+
"comment cluster version",
|
|
186
243
|
() => {
|
|
187
244
|
clusterModel.runtime.onCamera(editor.getZoomLevel());
|
|
188
|
-
return clusterModel.runtime.
|
|
245
|
+
return clusterModel.runtime.version;
|
|
189
246
|
},
|
|
190
247
|
[clusterModel, editor]
|
|
191
248
|
);
|
|
192
249
|
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]);
|
|
250
|
+
return Array.from(clusterModel.runtime.getVisible().values());
|
|
251
|
+
}, [clusterModel, partitionVersion]);
|
|
199
252
|
const fadeNodes = useFadeVisibleNodes(visibleNodes, clusterModel);
|
|
200
253
|
const threadsById = useMemo(
|
|
201
254
|
() => new Map(threads.map((thread) => [thread.id, thread])),
|
|
@@ -254,7 +307,7 @@ function CanvasCommentsLayer(props) {
|
|
|
254
307
|
if (e.key !== "Escape" || openThreadId.get(editor) === null) return;
|
|
255
308
|
if (isMentionPickerOpen()) return;
|
|
256
309
|
const target = e.target;
|
|
257
|
-
if (target && target.closest(".cmt-editing")) return;
|
|
310
|
+
if (target && target.closest(".tlui-cmt-editing")) return;
|
|
258
311
|
openThreadId.set(editor, null);
|
|
259
312
|
e.preventDefault();
|
|
260
313
|
e.stopPropagation();
|
|
@@ -275,7 +328,7 @@ function CanvasCommentsLayer(props) {
|
|
|
275
328
|
}, [editor]);
|
|
276
329
|
if (hidden) return null;
|
|
277
330
|
return createPortal(
|
|
278
|
-
/* @__PURE__ */ jsxs("div", { ref: layerRef, className: "cmt-canvas-layer", children: [
|
|
331
|
+
/* @__PURE__ */ jsxs("div", { ref: layerRef, className: "tlui-cmt-canvas-layer", children: [
|
|
279
332
|
options.enableClustering ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
280
333
|
fadeNodes.map(({ node, phase }) => {
|
|
281
334
|
let content;
|
|
@@ -306,7 +359,7 @@ function CanvasCommentsLayer(props) {
|
|
|
306
359
|
},
|
|
307
360
|
thread.id
|
|
308
361
|
)),
|
|
309
|
-
|
|
362
|
+
heldThreads.map((thread) => /* @__PURE__ */ jsx(
|
|
310
363
|
ThreadPin,
|
|
311
364
|
{
|
|
312
365
|
editor,
|
|
@@ -415,7 +468,7 @@ function cancelClusterFadeFrame(frame) {
|
|
|
415
468
|
else window.clearTimeout(frame);
|
|
416
469
|
}
|
|
417
470
|
function clusterFadeClassName(phase) {
|
|
418
|
-
return `cmt-cluster-fade cmt-cluster-fade--${phase}`;
|
|
471
|
+
return `tlui-cmt-cluster-fade tlui-cmt-cluster-fade--${phase}`;
|
|
419
472
|
}
|
|
420
473
|
function findMovedClusteredLeafIds(rendered, latest) {
|
|
421
474
|
if (rendered.table === latest.table) return [];
|
|
@@ -432,11 +485,6 @@ function findMovedClusteredLeafIds(rendered, latest) {
|
|
|
432
485
|
}
|
|
433
486
|
return moved;
|
|
434
487
|
}
|
|
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
488
|
function getClusterZoomBounds(editor) {
|
|
441
489
|
const cameraOptions = editor.getCameraOptions();
|
|
442
490
|
const baseZoom = cameraOptions.constraints ? editor.getBaseZoom() : 1;
|
|
@@ -488,6 +536,9 @@ const ClusterBadge = memo(function ClusterBadge2({
|
|
|
488
536
|
node,
|
|
489
537
|
onExpand
|
|
490
538
|
}) {
|
|
539
|
+
const container = useContainer();
|
|
540
|
+
const badgeRef = useRef(null);
|
|
541
|
+
usePassThroughWheelEvents(badgeRef);
|
|
491
542
|
const point = useValue(
|
|
492
543
|
"cluster badge point",
|
|
493
544
|
() => {
|
|
@@ -501,9 +552,16 @@ const ClusterBadge = memo(function ClusterBadge2({
|
|
|
501
552
|
return /* @__PURE__ */ jsx(
|
|
502
553
|
"div",
|
|
503
554
|
{
|
|
504
|
-
|
|
555
|
+
ref: badgeRef,
|
|
556
|
+
className: "tlui-cmt-canvas-cluster",
|
|
505
557
|
style: { left: point.x, top: point.y },
|
|
506
|
-
onPointerDown:
|
|
558
|
+
onPointerDown: (e) => {
|
|
559
|
+
if (isCanvasPanGesture(editor, e)) {
|
|
560
|
+
forwardPointerEventToCanvas(container, e);
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
e.stopPropagation();
|
|
564
|
+
},
|
|
507
565
|
onClick: (e) => {
|
|
508
566
|
e.stopPropagation();
|
|
509
567
|
onExpand(node);
|
|
@@ -526,7 +584,19 @@ function ThreadPopover({
|
|
|
526
584
|
usePassThroughWheelEvents(ref);
|
|
527
585
|
usePassThroughMouseOverEvents(ref);
|
|
528
586
|
return createPortal(
|
|
529
|
-
|
|
587
|
+
// contextmenu also stops here: portals bubble React events to the canvas's context-menu
|
|
588
|
+
// trigger (the layer mounts inside it), which would open the canvas menu over this panel.
|
|
589
|
+
/* @__PURE__ */ jsx(
|
|
590
|
+
"div",
|
|
591
|
+
{
|
|
592
|
+
ref,
|
|
593
|
+
className: "tlui-cmt-canvas-popover",
|
|
594
|
+
style,
|
|
595
|
+
onPointerDown: stop,
|
|
596
|
+
onContextMenu: stop,
|
|
597
|
+
children
|
|
598
|
+
}
|
|
599
|
+
),
|
|
530
600
|
container
|
|
531
601
|
);
|
|
532
602
|
}
|
|
@@ -547,12 +617,19 @@ function RegionBox({
|
|
|
547
617
|
[editor, box.x, box.y, box.w, box.h]
|
|
548
618
|
);
|
|
549
619
|
const grabRef = useRef(null);
|
|
620
|
+
const container = useContainer();
|
|
621
|
+
const boxRef = useRef(null);
|
|
622
|
+
usePassThroughWheelEvents(boxRef);
|
|
550
623
|
const translated = (e) => {
|
|
551
624
|
const g = grabRef.current;
|
|
552
625
|
const p = editor.screenToPage({ x: e.clientX, y: e.clientY });
|
|
553
626
|
return { ...g.box, x: g.box.x + (p.x - g.page.x), y: g.box.y + (p.y - g.page.y) };
|
|
554
627
|
};
|
|
555
628
|
const startMove = (e) => {
|
|
629
|
+
if (isCanvasPanGesture(editor, e)) {
|
|
630
|
+
forwardPointerEventToCanvas(container, e);
|
|
631
|
+
return;
|
|
632
|
+
}
|
|
556
633
|
e.stopPropagation();
|
|
557
634
|
grabRef.current = { page: editor.screenToPage({ x: e.clientX, y: e.clientY }), box };
|
|
558
635
|
e.currentTarget.setPointerCapture(e.pointerId);
|
|
@@ -571,7 +648,8 @@ function RegionBox({
|
|
|
571
648
|
return /* @__PURE__ */ jsx(
|
|
572
649
|
"div",
|
|
573
650
|
{
|
|
574
|
-
|
|
651
|
+
ref: boxRef,
|
|
652
|
+
className: movable ? "tlui-cmt-canvas-region tlui-cmt-canvas-region--movable" : "tlui-cmt-canvas-region",
|
|
575
653
|
style: rect,
|
|
576
654
|
onPointerDown: movable ? startMove : void 0,
|
|
577
655
|
onPointerMove: movable ? onMove : void 0,
|
|
@@ -645,7 +723,7 @@ function RegionResizeHandles({
|
|
|
645
723
|
return /* @__PURE__ */ jsx(Fragment, { children: points.map((h) => /* @__PURE__ */ jsx(
|
|
646
724
|
"div",
|
|
647
725
|
{
|
|
648
|
-
className: "cmt-canvas-region-handle",
|
|
726
|
+
className: "tlui-cmt-canvas-region-handle",
|
|
649
727
|
style: { left: h.left, top: h.top, cursor: h.cursor },
|
|
650
728
|
onPointerDown: startResize,
|
|
651
729
|
onPointerMove: onResize(h),
|
|
@@ -662,13 +740,15 @@ const ThreadPin = memo(function ThreadPin2({
|
|
|
662
740
|
}) {
|
|
663
741
|
const {
|
|
664
742
|
currentUserId,
|
|
665
|
-
|
|
743
|
+
resolveAuthor,
|
|
666
744
|
onPostComment,
|
|
667
745
|
isCommentUnread,
|
|
668
746
|
onCommentRead,
|
|
669
747
|
getMentionSuggestions,
|
|
670
748
|
renderMentionSuggestion
|
|
671
749
|
} = props;
|
|
750
|
+
const resolveName = useCallback((id) => resolveAuthor(id)?.name, [resolveAuthor]);
|
|
751
|
+
const me = currentUserId ? resolveAuthor(currentUserId) : void 0;
|
|
672
752
|
const options = useCommentingOptions();
|
|
673
753
|
const impreciseShapeAnchor = props.impreciseShapeAnchor ?? options.impreciseShapeAnchor;
|
|
674
754
|
const container = useContainer();
|
|
@@ -678,7 +758,9 @@ const ThreadPin = memo(function ThreadPin2({
|
|
|
678
758
|
editor,
|
|
679
759
|
thread.id
|
|
680
760
|
]);
|
|
681
|
-
const [reply, setReply] = useState(
|
|
761
|
+
const [reply, setReply] = useState(
|
|
762
|
+
() => getCommentDraft(replyDraftSlot(thread.id)) ?? EMPTY_COMMENT
|
|
763
|
+
);
|
|
682
764
|
const [editingId, setEditingId] = useState(null);
|
|
683
765
|
const [editText, setEditText] = useState(EMPTY_COMMENT);
|
|
684
766
|
const [dragPagePoint, setDragPagePoint] = useState(null);
|
|
@@ -697,24 +779,29 @@ const ThreadPin = memo(function ThreadPin2({
|
|
|
697
779
|
[editor, thread.anchor, thread.pageId]
|
|
698
780
|
);
|
|
699
781
|
const revealed = open || resizeBounds != null || regionOptions.reveal === "pointer" && pointerInRegion || regionOptions.reveal === "pin-hover" && pinHovered;
|
|
782
|
+
const pinCorner = thread.anchor.type === "region" ? regionAnchorPinCorner(editor, thread.anchor) : regionOptions.pinCorner;
|
|
700
783
|
const resizeHandles = useMemo(
|
|
701
|
-
() => regionOptions.resize === "edges" ? REGION_EDGES : REGION_CORNERS.filter(
|
|
702
|
-
|
|
703
|
-
),
|
|
704
|
-
[regionOptions.resize, regionOptions.pinCorner]
|
|
784
|
+
() => regionOptions.resize === "edges" ? REGION_EDGES : REGION_CORNERS.filter((c) => c.x !== pinCorner.x || c.y !== pinCorner.y),
|
|
785
|
+
[regionOptions.resize, pinCorner]
|
|
705
786
|
);
|
|
706
787
|
const dragRef = useRef(null);
|
|
707
788
|
const markerRef = useRef(null);
|
|
789
|
+
usePassThroughWheelEvents(markerRef);
|
|
790
|
+
useEffect(() => {
|
|
791
|
+
return () => {
|
|
792
|
+
if (dragRef.current) editor.setHintingShapes([]);
|
|
793
|
+
};
|
|
794
|
+
}, [editor]);
|
|
708
795
|
useEffect(() => {
|
|
709
796
|
if (!open) return;
|
|
710
797
|
const onPointerDown = (e) => {
|
|
711
798
|
const target = e.target;
|
|
712
799
|
if (!target) return;
|
|
713
|
-
if (target.closest(".cmt-canvas-popover")) return;
|
|
800
|
+
if (target.closest(".tlui-cmt-canvas-popover")) return;
|
|
714
801
|
const marker = markerRef.current;
|
|
715
802
|
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"))
|
|
803
|
+
if (target.closest(".tlui-cmt-canvas-region-handle, .tlui-cmt-canvas-region--movable")) return;
|
|
804
|
+
if (target.closest(".tlui-menu, [data-radix-popper-content-wrapper], .tlui-cmt-mention-popup"))
|
|
718
805
|
return;
|
|
719
806
|
openThreadId.set(editor, null);
|
|
720
807
|
};
|
|
@@ -726,7 +813,10 @@ const ThreadPin = memo(function ThreadPin2({
|
|
|
726
813
|
() => {
|
|
727
814
|
if (thread.pageId !== editor.getCurrentPageId()) return null;
|
|
728
815
|
const pagePoint = anchorPagePoint(editor, thread.anchor, impreciseShapeAnchor);
|
|
729
|
-
|
|
816
|
+
if (!pagePoint) return null;
|
|
817
|
+
const viewportPoint = editor.pageToViewport(pagePoint);
|
|
818
|
+
const inset = impreciseShapePinInset(thread.anchor, impreciseShapeAnchor);
|
|
819
|
+
return inset ? { x: viewportPoint.x + inset.x, y: viewportPoint.y + inset.y } : viewportPoint;
|
|
730
820
|
},
|
|
731
821
|
[editor, thread.anchor, thread.pageId, impreciseShapeAnchor]
|
|
732
822
|
);
|
|
@@ -753,6 +843,7 @@ const ThreadPin = memo(function ThreadPin2({
|
|
|
753
843
|
if (onPostComment) onPostComment(comment);
|
|
754
844
|
});
|
|
755
845
|
setReply(EMPTY_COMMENT);
|
|
846
|
+
clearCommentDraft(replyDraftSlot(thread.id));
|
|
756
847
|
};
|
|
757
848
|
const toggleResolve = () => {
|
|
758
849
|
if (!currentUserId) return;
|
|
@@ -776,6 +867,16 @@ const ThreadPin = memo(function ThreadPin2({
|
|
|
776
867
|
setEditingId(comment.id);
|
|
777
868
|
setEditText(comment.body);
|
|
778
869
|
};
|
|
870
|
+
const deleteComment = (comment) => {
|
|
871
|
+
commitCommentMutation(editor, () => {
|
|
872
|
+
if (comments.length === 1) {
|
|
873
|
+
openThreadId.set(editor, null);
|
|
874
|
+
removeCommentRecords(editor, [thread.id, comment.id]);
|
|
875
|
+
} else {
|
|
876
|
+
removeCommentRecords(editor, [comment.id]);
|
|
877
|
+
}
|
|
878
|
+
});
|
|
879
|
+
};
|
|
779
880
|
const saveEdit = () => {
|
|
780
881
|
const comment = comments.find((c) => c.id === editingId);
|
|
781
882
|
if (!comment || isCommentEmpty(editText)) return;
|
|
@@ -790,7 +891,7 @@ const ThreadPin = memo(function ThreadPin2({
|
|
|
790
891
|
return /* @__PURE__ */ jsx(
|
|
791
892
|
"div",
|
|
792
893
|
{
|
|
793
|
-
className: "cmt-editing",
|
|
894
|
+
className: "tlui-cmt-editing",
|
|
794
895
|
onKeyDown: (e) => {
|
|
795
896
|
if (e.key === "Escape") {
|
|
796
897
|
setEditingId(null);
|
|
@@ -819,24 +920,54 @@ const ThreadPin = memo(function ThreadPin2({
|
|
|
819
920
|
CommentCard,
|
|
820
921
|
{
|
|
821
922
|
...card,
|
|
822
|
-
actions: comment.authorId === currentUserId ? /* @__PURE__ */
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
923
|
+
actions: comment.authorId === currentUserId ? /* @__PURE__ */ jsxs(TldrawUiDropdownMenuRoot, { id: `comment-actions-${comment.id}`, children: [
|
|
924
|
+
/* @__PURE__ */ jsx(TldrawUiDropdownMenuTrigger, { children: /* @__PURE__ */ jsx(
|
|
925
|
+
TooltipButton,
|
|
926
|
+
{
|
|
927
|
+
tooltip: msg("comments.more-options"),
|
|
928
|
+
className: "tlui-cmt-thread__action",
|
|
929
|
+
children: /* @__PURE__ */ jsx(TldrawUiIcon, { icon: "dots-vertical", label: msg("comments.more-options"), small: true })
|
|
930
|
+
}
|
|
931
|
+
) }),
|
|
932
|
+
/* @__PURE__ */ jsx(
|
|
933
|
+
TldrawUiDropdownMenuContent,
|
|
934
|
+
{
|
|
935
|
+
className: "tlui-cmt-menu",
|
|
936
|
+
side: "bottom",
|
|
937
|
+
align: "end",
|
|
938
|
+
alignOffset: 0,
|
|
939
|
+
children: /* @__PURE__ */ jsxs(TldrawUiDropdownMenuGroup, { children: [
|
|
940
|
+
/* @__PURE__ */ jsx(TldrawUiDropdownMenuItem, { children: /* @__PURE__ */ jsx(
|
|
941
|
+
"button",
|
|
942
|
+
{
|
|
943
|
+
type: "button",
|
|
944
|
+
className: "tlui-cmt-menu-item",
|
|
945
|
+
onClick: () => startEdit(comment),
|
|
946
|
+
children: /* @__PURE__ */ jsx("span", { children: msg("comments.edit-comment") })
|
|
947
|
+
}
|
|
948
|
+
) }),
|
|
949
|
+
/* @__PURE__ */ jsx(TldrawUiDropdownMenuItem, { children: /* @__PURE__ */ jsx(
|
|
950
|
+
"button",
|
|
951
|
+
{
|
|
952
|
+
type: "button",
|
|
953
|
+
className: "tlui-cmt-menu-item tlui-cmt-menu-item--danger",
|
|
954
|
+
onClick: () => deleteComment(comment),
|
|
955
|
+
children: /* @__PURE__ */ jsx("span", { children: msg("comments.delete-comment") })
|
|
956
|
+
}
|
|
957
|
+
) })
|
|
958
|
+
] })
|
|
959
|
+
}
|
|
960
|
+
)
|
|
961
|
+
] }) : void 0
|
|
831
962
|
}
|
|
832
963
|
);
|
|
833
964
|
};
|
|
834
965
|
const headerActions = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
835
966
|
currentUserId && /* @__PURE__ */ jsx(
|
|
836
|
-
|
|
967
|
+
TooltipButton,
|
|
837
968
|
{
|
|
838
|
-
|
|
839
|
-
|
|
969
|
+
tooltip: msg(thread.resolved ? "comments.reopen" : "comments.resolve"),
|
|
970
|
+
className: "tlui-cmt-thread__action",
|
|
840
971
|
onClick: toggleResolve,
|
|
841
972
|
children: /* @__PURE__ */ jsx(
|
|
842
973
|
TldrawUiIcon,
|
|
@@ -848,33 +979,62 @@ const ThreadPin = memo(function ThreadPin2({
|
|
|
848
979
|
)
|
|
849
980
|
}
|
|
850
981
|
),
|
|
851
|
-
currentUserId && /* @__PURE__ */
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
982
|
+
currentUserId && /* @__PURE__ */ jsxs(TldrawUiDropdownMenuRoot, { id: `comment-thread-actions-${thread.id}`, children: [
|
|
983
|
+
/* @__PURE__ */ jsx(TldrawUiDropdownMenuTrigger, { children: /* @__PURE__ */ jsx(
|
|
984
|
+
TooltipButton,
|
|
985
|
+
{
|
|
986
|
+
tooltip: msg("comments.more-options"),
|
|
987
|
+
className: "tlui-cmt-thread__action",
|
|
988
|
+
children: /* @__PURE__ */ jsx(TldrawUiIcon, { icon: "dots-vertical", label: msg("comments.more-options"), small: true })
|
|
989
|
+
}
|
|
990
|
+
) }),
|
|
991
|
+
/* @__PURE__ */ jsx(
|
|
992
|
+
TldrawUiDropdownMenuContent,
|
|
993
|
+
{
|
|
994
|
+
className: "tlui-cmt-menu",
|
|
995
|
+
side: "bottom",
|
|
996
|
+
align: "end",
|
|
997
|
+
alignOffset: 0,
|
|
998
|
+
children: /* @__PURE__ */ jsx(TldrawUiDropdownMenuGroup, { children: /* @__PURE__ */ jsx(TldrawUiDropdownMenuItem, { children: /* @__PURE__ */ jsx(
|
|
999
|
+
"button",
|
|
1000
|
+
{
|
|
1001
|
+
type: "button",
|
|
1002
|
+
className: "tlui-cmt-menu-item tlui-cmt-menu-item--danger",
|
|
1003
|
+
onClick: deleteThread,
|
|
1004
|
+
children: /* @__PURE__ */ jsx("span", { children: msg("comments.delete") })
|
|
1005
|
+
}
|
|
1006
|
+
) }) })
|
|
1007
|
+
}
|
|
1008
|
+
)
|
|
1009
|
+
] })
|
|
869
1010
|
] });
|
|
870
1011
|
const PinContent = options.components.PinContent;
|
|
871
|
-
const
|
|
1012
|
+
const threadAuthor = resolveAuthor(thread.createdBy);
|
|
1013
|
+
const pinContent = PinContent ? /* @__PURE__ */ jsx(PinContent, { thread, comments }) : initialOf(threadAuthor?.name ?? UNKNOWN_AUTHOR);
|
|
872
1014
|
const isRegion = thread.anchor.type === "region";
|
|
873
1015
|
const pinMovable = regionOptions.move !== "body";
|
|
874
1016
|
const bodyMovable = regionOptions.move !== "pin";
|
|
875
1017
|
const startDrag = (e) => {
|
|
1018
|
+
if (isCanvasPanGesture(editor, e)) {
|
|
1019
|
+
forwardPointerEventToCanvas(container, e);
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
876
1022
|
e.stopPropagation();
|
|
877
|
-
|
|
1023
|
+
const grabPage = editor.screenToPage({ x: e.clientX, y: e.clientY });
|
|
1024
|
+
const anchorPage = anchorPagePoint(editor, thread.anchor, impreciseShapeAnchor);
|
|
1025
|
+
const inset = impreciseShapePinInset(thread.anchor, impreciseShapeAnchor);
|
|
1026
|
+
if (anchorPage && inset) {
|
|
1027
|
+
const zoom = editor.getZoomLevel();
|
|
1028
|
+
anchorPage.x += inset.x / zoom;
|
|
1029
|
+
anchorPage.y += inset.y / zoom;
|
|
1030
|
+
}
|
|
1031
|
+
dragRef.current = {
|
|
1032
|
+
startX: e.clientX,
|
|
1033
|
+
startY: e.clientY,
|
|
1034
|
+
moved: false,
|
|
1035
|
+
offsetX: anchorPage ? anchorPage.x - grabPage.x : 0,
|
|
1036
|
+
offsetY: anchorPage ? anchorPage.y - grabPage.y : 0
|
|
1037
|
+
};
|
|
878
1038
|
e.currentTarget.setPointerCapture(e.pointerId);
|
|
879
1039
|
};
|
|
880
1040
|
const onDrag = (e) => {
|
|
@@ -883,7 +1043,23 @@ const ThreadPin = memo(function ThreadPin2({
|
|
|
883
1043
|
if (isRegion && !pinMovable) return;
|
|
884
1044
|
if (!drag.moved && Math.hypot(e.clientX - drag.startX, e.clientY - drag.startY) < 4) return;
|
|
885
1045
|
drag.moved = true;
|
|
886
|
-
|
|
1046
|
+
const cursorPage = editor.screenToPage({ x: e.clientX, y: e.clientY });
|
|
1047
|
+
const pagePoint = { x: cursorPage.x + drag.offsetX, y: cursorPage.y + drag.offsetY };
|
|
1048
|
+
setDragPagePoint(pagePoint);
|
|
1049
|
+
if (!isRegion) {
|
|
1050
|
+
const hit = editor.getShapeAtPoint(pagePoint, { hitInside: true });
|
|
1051
|
+
editor.setHintingShapes(hit ? [hit.id] : []);
|
|
1052
|
+
}
|
|
1053
|
+
};
|
|
1054
|
+
const cancelDrag = (e) => {
|
|
1055
|
+
const drag = dragRef.current;
|
|
1056
|
+
dragRef.current = null;
|
|
1057
|
+
if (e.currentTarget.hasPointerCapture(e.pointerId)) {
|
|
1058
|
+
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
1059
|
+
}
|
|
1060
|
+
if (!drag) return;
|
|
1061
|
+
setDragPagePoint(null);
|
|
1062
|
+
editor.setHintingShapes([]);
|
|
887
1063
|
};
|
|
888
1064
|
const endDrag = (e) => {
|
|
889
1065
|
const drag = dragRef.current;
|
|
@@ -892,38 +1068,51 @@ const ThreadPin = memo(function ThreadPin2({
|
|
|
892
1068
|
e.currentTarget.releasePointerCapture(e.pointerId);
|
|
893
1069
|
}
|
|
894
1070
|
if (!drag) return;
|
|
1071
|
+
editor.setHintingShapes([]);
|
|
895
1072
|
if (!drag.moved) {
|
|
896
1073
|
openThreadId.set(editor, openThreadId.get(editor) === thread.id ? null : thread.id);
|
|
897
1074
|
return;
|
|
898
1075
|
}
|
|
899
|
-
const
|
|
1076
|
+
const cursorPage = editor.screenToPage({ x: e.clientX, y: e.clientY });
|
|
1077
|
+
const pagePoint = { x: cursorPage.x + drag.offsetX, y: cursorPage.y + drag.offsetY };
|
|
900
1078
|
setDragPagePoint(null);
|
|
901
1079
|
let anchor;
|
|
902
1080
|
if (thread.anchor.type === "region") {
|
|
903
1081
|
anchor = {
|
|
904
1082
|
...thread.anchor,
|
|
905
|
-
x: pagePoint.x -
|
|
906
|
-
y: pagePoint.y -
|
|
1083
|
+
x: pagePoint.x - pinCorner.x * thread.anchor.w,
|
|
1084
|
+
y: pagePoint.y - pinCorner.y * thread.anchor.h
|
|
907
1085
|
};
|
|
908
1086
|
} else {
|
|
909
1087
|
const hit = editor.getShapeAtPoint(pagePoint, { hitInside: true });
|
|
910
|
-
anchor = hit ? shapeAnchorAt(
|
|
1088
|
+
anchor = hit ? shapeAnchorAt(
|
|
1089
|
+
editor,
|
|
1090
|
+
hit.id,
|
|
1091
|
+
pagePoint,
|
|
1092
|
+
getCommentingOptions(editor).shouldBePrecise(editor, {
|
|
1093
|
+
shapeId: hit.id,
|
|
1094
|
+
point: pagePoint,
|
|
1095
|
+
altKey: e.altKey
|
|
1096
|
+
})
|
|
1097
|
+
) : { type: "point", x: pagePoint.x, y: pagePoint.y };
|
|
911
1098
|
}
|
|
912
1099
|
commitCommentMutation(editor, () => putCommentRecords(editor, [{ ...thread, anchor }]), "drag");
|
|
913
1100
|
};
|
|
914
|
-
const livePinPage = resizeBounds ? regionPinPoint(resizeBounds,
|
|
915
|
-
const
|
|
1101
|
+
const livePinPage = resizeBounds ? regionPinPoint(resizeBounds, pinCorner) : dragPagePoint;
|
|
1102
|
+
const renderPointBase = livePinPage ? editor.pageToViewport(livePinPage) : point;
|
|
1103
|
+
const renderPoint = isRegion ? { x: renderPointBase.x - 17, y: renderPointBase.y + 17 } : renderPointBase;
|
|
916
1104
|
const regionAnchor = thread.anchor.type === "region" ? thread.anchor : void 0;
|
|
917
1105
|
const movedRegion = regionAnchor && dragPagePoint ? {
|
|
918
1106
|
...regionAnchor,
|
|
919
|
-
x: dragPagePoint.x -
|
|
920
|
-
y: dragPagePoint.y -
|
|
1107
|
+
x: dragPagePoint.x - pinCorner.x * regionAnchor.w,
|
|
1108
|
+
y: dragPagePoint.y - pinCorner.y * regionAnchor.h
|
|
921
1109
|
} : regionAnchor;
|
|
922
1110
|
const regionBoxBounds = resizeBounds ?? movedRegion;
|
|
923
1111
|
const commitResize = (bounds) => {
|
|
924
1112
|
setResizeBounds(null);
|
|
925
1113
|
editor.run(
|
|
926
|
-
|
|
1114
|
+
// Spread the existing anchor first so the region's pin corner survives a resize.
|
|
1115
|
+
() => putCommentRecords(editor, [{ ...thread, anchor: { ...regionAnchor, ...bounds } }]),
|
|
927
1116
|
{
|
|
928
1117
|
history: "ignore"
|
|
929
1118
|
}
|
|
@@ -953,44 +1142,52 @@ const ThreadPin = memo(function ThreadPin2({
|
|
|
953
1142
|
/* @__PURE__ */ jsxs(
|
|
954
1143
|
"div",
|
|
955
1144
|
{
|
|
956
|
-
className:
|
|
1145
|
+
className: [
|
|
1146
|
+
"tlui-cmt-canvas-pin",
|
|
1147
|
+
open && "tlui-cmt-canvas-pin--open",
|
|
1148
|
+
dragPagePoint && "tlui-cmt-canvas-pin--dragging"
|
|
1149
|
+
].filter(Boolean).join(" "),
|
|
957
1150
|
style: { left: renderPoint.x, top: renderPoint.y },
|
|
958
1151
|
children: [
|
|
959
1152
|
/* @__PURE__ */ jsx(
|
|
960
1153
|
"div",
|
|
961
1154
|
{
|
|
962
1155
|
ref: markerRef,
|
|
963
|
-
className: "cmt-canvas-pin__marker",
|
|
1156
|
+
className: "tlui-cmt-canvas-pin__marker",
|
|
964
1157
|
onPointerDown: startDrag,
|
|
965
1158
|
onPointerMove: onDrag,
|
|
966
1159
|
onPointerUp: endDrag,
|
|
1160
|
+
onPointerCancel: cancelDrag,
|
|
967
1161
|
onPointerEnter: () => setPinHovered(true),
|
|
968
1162
|
onPointerLeave: () => setPinHovered(false),
|
|
969
|
-
children: /* @__PURE__ */ jsx(CommentPin, { resolved: thread.resolved != null, open, children: pinContent })
|
|
1163
|
+
children: /* @__PURE__ */ jsx(CommentPin, { resolved: thread.resolved != null, open, color: threadAuthor?.color, children: pinContent })
|
|
970
1164
|
}
|
|
971
1165
|
),
|
|
972
1166
|
open && /* @__PURE__ */ jsx(
|
|
973
1167
|
ThreadPopover,
|
|
974
1168
|
{
|
|
975
1169
|
container,
|
|
976
|
-
style: { left: renderPoint.x +
|
|
1170
|
+
style: { left: renderPoint.x + 48, top: renderPoint.y - 54 },
|
|
977
1171
|
children: /* @__PURE__ */ jsx(
|
|
978
1172
|
CommentThread,
|
|
979
1173
|
{
|
|
980
1174
|
header: msg("comments.thread-title"),
|
|
981
1175
|
headerActions,
|
|
982
1176
|
renderComment,
|
|
983
|
-
comments: comments.map((c) => toCardProps(c, props, options.components)),
|
|
1177
|
+
comments: comments.map((c) => toCardProps(c, props, options.components, resolveName)),
|
|
984
1178
|
resolvedBanner: thread.resolved ? msg("comments.resolved-by").replace(
|
|
985
1179
|
"{name}",
|
|
986
|
-
|
|
1180
|
+
resolveAuthor(thread.resolved.by)?.name ?? UNKNOWN_AUTHOR
|
|
987
1181
|
) : void 0,
|
|
988
1182
|
composer: currentUserId && !thread.resolved ? {
|
|
989
|
-
author:
|
|
1183
|
+
author: me ?? UNKNOWN_COMMENT_AUTHOR,
|
|
990
1184
|
placeholder: msg("comments.reply-placeholder"),
|
|
991
1185
|
sendLabel: msg("comments.send"),
|
|
992
1186
|
value: reply,
|
|
993
|
-
onChange:
|
|
1187
|
+
onChange: (value) => {
|
|
1188
|
+
setReply(value);
|
|
1189
|
+
saveCommentDraft(replyDraftSlot(thread.id), value);
|
|
1190
|
+
},
|
|
994
1191
|
onSubmit: postReply,
|
|
995
1192
|
disabled: isCommentEmpty(reply),
|
|
996
1193
|
getMentionSuggestions,
|
|
@@ -1009,12 +1206,15 @@ function PendingComposer({
|
|
|
1009
1206
|
editor,
|
|
1010
1207
|
pending,
|
|
1011
1208
|
currentUserId,
|
|
1012
|
-
|
|
1209
|
+
resolveAuthor,
|
|
1013
1210
|
onPostComment,
|
|
1014
1211
|
getMentionSuggestions,
|
|
1015
1212
|
renderMentionSuggestion
|
|
1016
1213
|
}) {
|
|
1017
|
-
const
|
|
1214
|
+
const me = currentUserId ? resolveAuthor(currentUserId) : void 0;
|
|
1215
|
+
const [text, setText] = useState(
|
|
1216
|
+
() => getCommentDraft(NEW_COMMENT_DRAFT) ?? EMPTY_COMMENT
|
|
1217
|
+
);
|
|
1018
1218
|
const ref = useRef(null);
|
|
1019
1219
|
const msg = useTranslation();
|
|
1020
1220
|
const container = useContainer();
|
|
@@ -1029,7 +1229,7 @@ function PendingComposer({
|
|
|
1029
1229
|
const el = ref.current;
|
|
1030
1230
|
const target = e.target;
|
|
1031
1231
|
if (!el || !target) return;
|
|
1032
|
-
if (el.contains(target) || target.closest(".cmt-mention-popup")) return;
|
|
1232
|
+
if (el.contains(target) || target.closest(".tlui-cmt-mention-popup")) return;
|
|
1033
1233
|
pendingComment.set(editor, null);
|
|
1034
1234
|
};
|
|
1035
1235
|
document.addEventListener("pointerdown", onPointerDown, true);
|
|
@@ -1054,6 +1254,7 @@ function PendingComposer({
|
|
|
1054
1254
|
if (onPostComment) onPostComment(comment);
|
|
1055
1255
|
});
|
|
1056
1256
|
setText(EMPTY_COMMENT);
|
|
1257
|
+
clearCommentDraft(NEW_COMMENT_DRAFT);
|
|
1057
1258
|
pendingComment.set(editor, null);
|
|
1058
1259
|
};
|
|
1059
1260
|
return createPortal(
|
|
@@ -1061,26 +1262,33 @@ function PendingComposer({
|
|
|
1061
1262
|
"div",
|
|
1062
1263
|
{
|
|
1063
1264
|
ref,
|
|
1064
|
-
className:
|
|
1265
|
+
className: [
|
|
1266
|
+
"tlui-cmt-canvas-composer",
|
|
1267
|
+
pending.anchor.type === "region" && "tlui-cmt-canvas-composer--region"
|
|
1268
|
+
].filter(Boolean).join(" "),
|
|
1065
1269
|
style: { left: point.x, top: point.y },
|
|
1066
1270
|
onPointerDown: stop,
|
|
1271
|
+
onContextMenu: stop,
|
|
1067
1272
|
onKeyDown: (e) => {
|
|
1068
1273
|
if (e.key === "Escape" && !isMentionPickerOpen()) pendingComment.set(editor, null);
|
|
1069
1274
|
},
|
|
1070
1275
|
children: /* @__PURE__ */ jsx(
|
|
1071
1276
|
CommentComposer,
|
|
1072
1277
|
{
|
|
1073
|
-
author:
|
|
1278
|
+
author: me ?? UNKNOWN_COMMENT_AUTHOR,
|
|
1074
1279
|
placeholder: msg("comments.add-placeholder"),
|
|
1075
1280
|
sendLabel: msg("comments.send"),
|
|
1076
1281
|
value: text,
|
|
1077
|
-
onChange:
|
|
1282
|
+
onChange: (value) => {
|
|
1283
|
+
setText(value);
|
|
1284
|
+
saveCommentDraft(NEW_COMMENT_DRAFT, value);
|
|
1285
|
+
},
|
|
1078
1286
|
onSubmit: submit,
|
|
1079
1287
|
disabled: isCommentEmpty(text),
|
|
1080
1288
|
getMentionSuggestions,
|
|
1081
1289
|
renderMentionSuggestion,
|
|
1082
1290
|
autoFocus: true,
|
|
1083
|
-
leading: draftAvatar
|
|
1291
|
+
leading: draftAvatar(me?.color)
|
|
1084
1292
|
}
|
|
1085
1293
|
)
|
|
1086
1294
|
}
|