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