@tldraw/commenting 0.0.0-bootstrap → 5.3.0-internal.41291fb579ed
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 +55 -11
- 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 +294 -111
- 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/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-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 +56 -12
- 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 +312 -113
- 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/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/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 +87 -15
- package/src/canvas/comments-filter-menu.tsx +27 -33
- package/src/canvas/comments-overflow-menu.tsx +13 -21
- package/src/canvas/comments-overlay.tsx +431 -144
- package/src/canvas/comments-sidebar.tsx +27 -28
- package/src/canvas/comments-visibility-toggle.tsx +50 -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 +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,18 +1068,20 @@ 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 });
|
|
@@ -911,19 +1089,21 @@ const ThreadPin = memo(function ThreadPin2({
|
|
|
911
1089
|
}
|
|
912
1090
|
commitCommentMutation(editor, () => putCommentRecords(editor, [{ ...thread, anchor }]), "drag");
|
|
913
1091
|
};
|
|
914
|
-
const livePinPage = resizeBounds ? regionPinPoint(resizeBounds,
|
|
915
|
-
const
|
|
1092
|
+
const livePinPage = resizeBounds ? regionPinPoint(resizeBounds, pinCorner) : dragPagePoint;
|
|
1093
|
+
const renderPointBase = livePinPage ? editor.pageToViewport(livePinPage) : point;
|
|
1094
|
+
const renderPoint = isRegion ? { x: renderPointBase.x - 17, y: renderPointBase.y + 17 } : renderPointBase;
|
|
916
1095
|
const regionAnchor = thread.anchor.type === "region" ? thread.anchor : void 0;
|
|
917
1096
|
const movedRegion = regionAnchor && dragPagePoint ? {
|
|
918
1097
|
...regionAnchor,
|
|
919
|
-
x: dragPagePoint.x -
|
|
920
|
-
y: dragPagePoint.y -
|
|
1098
|
+
x: dragPagePoint.x - pinCorner.x * regionAnchor.w,
|
|
1099
|
+
y: dragPagePoint.y - pinCorner.y * regionAnchor.h
|
|
921
1100
|
} : regionAnchor;
|
|
922
1101
|
const regionBoxBounds = resizeBounds ?? movedRegion;
|
|
923
1102
|
const commitResize = (bounds) => {
|
|
924
1103
|
setResizeBounds(null);
|
|
925
1104
|
editor.run(
|
|
926
|
-
|
|
1105
|
+
// Spread the existing anchor first so the region's pin corner survives a resize.
|
|
1106
|
+
() => putCommentRecords(editor, [{ ...thread, anchor: { ...regionAnchor, ...bounds } }]),
|
|
927
1107
|
{
|
|
928
1108
|
history: "ignore"
|
|
929
1109
|
}
|
|
@@ -953,44 +1133,52 @@ const ThreadPin = memo(function ThreadPin2({
|
|
|
953
1133
|
/* @__PURE__ */ jsxs(
|
|
954
1134
|
"div",
|
|
955
1135
|
{
|
|
956
|
-
className:
|
|
1136
|
+
className: [
|
|
1137
|
+
"tlui-cmt-canvas-pin",
|
|
1138
|
+
open && "tlui-cmt-canvas-pin--open",
|
|
1139
|
+
dragPagePoint && "tlui-cmt-canvas-pin--dragging"
|
|
1140
|
+
].filter(Boolean).join(" "),
|
|
957
1141
|
style: { left: renderPoint.x, top: renderPoint.y },
|
|
958
1142
|
children: [
|
|
959
1143
|
/* @__PURE__ */ jsx(
|
|
960
1144
|
"div",
|
|
961
1145
|
{
|
|
962
1146
|
ref: markerRef,
|
|
963
|
-
className: "cmt-canvas-pin__marker",
|
|
1147
|
+
className: "tlui-cmt-canvas-pin__marker",
|
|
964
1148
|
onPointerDown: startDrag,
|
|
965
1149
|
onPointerMove: onDrag,
|
|
966
1150
|
onPointerUp: endDrag,
|
|
1151
|
+
onPointerCancel: cancelDrag,
|
|
967
1152
|
onPointerEnter: () => setPinHovered(true),
|
|
968
1153
|
onPointerLeave: () => setPinHovered(false),
|
|
969
|
-
children: /* @__PURE__ */ jsx(CommentPin, { resolved: thread.resolved != null, open, children: pinContent })
|
|
1154
|
+
children: /* @__PURE__ */ jsx(CommentPin, { resolved: thread.resolved != null, open, color: threadAuthor?.color, children: pinContent })
|
|
970
1155
|
}
|
|
971
1156
|
),
|
|
972
1157
|
open && /* @__PURE__ */ jsx(
|
|
973
1158
|
ThreadPopover,
|
|
974
1159
|
{
|
|
975
1160
|
container,
|
|
976
|
-
style: { left: renderPoint.x +
|
|
1161
|
+
style: { left: renderPoint.x + 48, top: renderPoint.y - 54 },
|
|
977
1162
|
children: /* @__PURE__ */ jsx(
|
|
978
1163
|
CommentThread,
|
|
979
1164
|
{
|
|
980
1165
|
header: msg("comments.thread-title"),
|
|
981
1166
|
headerActions,
|
|
982
1167
|
renderComment,
|
|
983
|
-
comments: comments.map((c) => toCardProps(c, props, options.components)),
|
|
1168
|
+
comments: comments.map((c) => toCardProps(c, props, options.components, resolveName)),
|
|
984
1169
|
resolvedBanner: thread.resolved ? msg("comments.resolved-by").replace(
|
|
985
1170
|
"{name}",
|
|
986
|
-
|
|
1171
|
+
resolveAuthor(thread.resolved.by)?.name ?? UNKNOWN_AUTHOR
|
|
987
1172
|
) : void 0,
|
|
988
1173
|
composer: currentUserId && !thread.resolved ? {
|
|
989
|
-
author:
|
|
1174
|
+
author: me ?? UNKNOWN_COMMENT_AUTHOR,
|
|
990
1175
|
placeholder: msg("comments.reply-placeholder"),
|
|
991
1176
|
sendLabel: msg("comments.send"),
|
|
992
1177
|
value: reply,
|
|
993
|
-
onChange:
|
|
1178
|
+
onChange: (value) => {
|
|
1179
|
+
setReply(value);
|
|
1180
|
+
saveCommentDraft(replyDraftSlot(thread.id), value);
|
|
1181
|
+
},
|
|
994
1182
|
onSubmit: postReply,
|
|
995
1183
|
disabled: isCommentEmpty(reply),
|
|
996
1184
|
getMentionSuggestions,
|
|
@@ -1009,12 +1197,15 @@ function PendingComposer({
|
|
|
1009
1197
|
editor,
|
|
1010
1198
|
pending,
|
|
1011
1199
|
currentUserId,
|
|
1012
|
-
|
|
1200
|
+
resolveAuthor,
|
|
1013
1201
|
onPostComment,
|
|
1014
1202
|
getMentionSuggestions,
|
|
1015
1203
|
renderMentionSuggestion
|
|
1016
1204
|
}) {
|
|
1017
|
-
const
|
|
1205
|
+
const me = currentUserId ? resolveAuthor(currentUserId) : void 0;
|
|
1206
|
+
const [text, setText] = useState(
|
|
1207
|
+
() => getCommentDraft(NEW_COMMENT_DRAFT) ?? EMPTY_COMMENT
|
|
1208
|
+
);
|
|
1018
1209
|
const ref = useRef(null);
|
|
1019
1210
|
const msg = useTranslation();
|
|
1020
1211
|
const container = useContainer();
|
|
@@ -1029,7 +1220,7 @@ function PendingComposer({
|
|
|
1029
1220
|
const el = ref.current;
|
|
1030
1221
|
const target = e.target;
|
|
1031
1222
|
if (!el || !target) return;
|
|
1032
|
-
if (el.contains(target) || target.closest(".cmt-mention-popup")) return;
|
|
1223
|
+
if (el.contains(target) || target.closest(".tlui-cmt-mention-popup")) return;
|
|
1033
1224
|
pendingComment.set(editor, null);
|
|
1034
1225
|
};
|
|
1035
1226
|
document.addEventListener("pointerdown", onPointerDown, true);
|
|
@@ -1054,6 +1245,7 @@ function PendingComposer({
|
|
|
1054
1245
|
if (onPostComment) onPostComment(comment);
|
|
1055
1246
|
});
|
|
1056
1247
|
setText(EMPTY_COMMENT);
|
|
1248
|
+
clearCommentDraft(NEW_COMMENT_DRAFT);
|
|
1057
1249
|
pendingComment.set(editor, null);
|
|
1058
1250
|
};
|
|
1059
1251
|
return createPortal(
|
|
@@ -1061,26 +1253,33 @@ function PendingComposer({
|
|
|
1061
1253
|
"div",
|
|
1062
1254
|
{
|
|
1063
1255
|
ref,
|
|
1064
|
-
className:
|
|
1256
|
+
className: [
|
|
1257
|
+
"tlui-cmt-canvas-composer",
|
|
1258
|
+
pending.anchor.type === "region" && "tlui-cmt-canvas-composer--region"
|
|
1259
|
+
].filter(Boolean).join(" "),
|
|
1065
1260
|
style: { left: point.x, top: point.y },
|
|
1066
1261
|
onPointerDown: stop,
|
|
1262
|
+
onContextMenu: stop,
|
|
1067
1263
|
onKeyDown: (e) => {
|
|
1068
1264
|
if (e.key === "Escape" && !isMentionPickerOpen()) pendingComment.set(editor, null);
|
|
1069
1265
|
},
|
|
1070
1266
|
children: /* @__PURE__ */ jsx(
|
|
1071
1267
|
CommentComposer,
|
|
1072
1268
|
{
|
|
1073
|
-
author:
|
|
1269
|
+
author: me ?? UNKNOWN_COMMENT_AUTHOR,
|
|
1074
1270
|
placeholder: msg("comments.add-placeholder"),
|
|
1075
1271
|
sendLabel: msg("comments.send"),
|
|
1076
1272
|
value: text,
|
|
1077
|
-
onChange:
|
|
1273
|
+
onChange: (value) => {
|
|
1274
|
+
setText(value);
|
|
1275
|
+
saveCommentDraft(NEW_COMMENT_DRAFT, value);
|
|
1276
|
+
},
|
|
1078
1277
|
onSubmit: submit,
|
|
1079
1278
|
disabled: isCommentEmpty(text),
|
|
1080
1279
|
getMentionSuggestions,
|
|
1081
1280
|
renderMentionSuggestion,
|
|
1082
1281
|
autoFocus: true,
|
|
1083
|
-
leading: draftAvatar
|
|
1282
|
+
leading: draftAvatar(me?.color)
|
|
1084
1283
|
}
|
|
1085
1284
|
)
|
|
1086
1285
|
}
|