@tldraw/commenting 5.6.0-next.c4e9c54a816e → 5.6.0-next.f151f7f3ebd6
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/dist-cjs/canvas/cluster-badge.js +7 -1
- package/dist-cjs/canvas/cluster-badge.js.map +2 -2
- package/dist-cjs/canvas/comment-reactions.js +10 -4
- package/dist-cjs/canvas/comment-reactions.js.map +2 -2
- package/dist-cjs/canvas/comment-render.js.map +2 -2
- package/dist-cjs/canvas/comment-tool.js +35 -15
- package/dist-cjs/canvas/comment-tool.js.map +2 -2
- package/dist-cjs/canvas/comments-filter-menu.js +13 -6
- package/dist-cjs/canvas/comments-filter-menu.js.map +2 -2
- package/dist-cjs/canvas/comments-menu-item.js +4 -1
- package/dist-cjs/canvas/comments-menu-item.js.map +2 -2
- package/dist-cjs/canvas/comments-overflow-menu.js +9 -4
- package/dist-cjs/canvas/comments-overflow-menu.js.map +2 -2
- package/dist-cjs/canvas/comments-sidebar.js +14 -5
- package/dist-cjs/canvas/comments-sidebar.js.map +2 -2
- package/dist-cjs/canvas/comments-visibility-toggle.js +5 -1
- package/dist-cjs/canvas/comments-visibility-toggle.js.map +2 -2
- package/dist-cjs/canvas/options.js.map +1 -1
- package/dist-cjs/canvas/pending-composer.js +7 -2
- package/dist-cjs/canvas/pending-composer.js.map +2 -2
- package/dist-cjs/canvas/thread-pin.js +13 -2
- package/dist-cjs/canvas/thread-pin.js.map +2 -2
- package/dist-cjs/canvas/thread-preview.js +4 -1
- package/dist-cjs/canvas/thread-preview.js.map +2 -2
- package/dist-cjs/canvas/thread-stack.js +18 -4
- package/dist-cjs/canvas/thread-stack.js.map +2 -2
- package/dist-cjs/canvas/thread-view.js +40 -20
- package/dist-cjs/canvas/thread-view.js.map +2 -2
- package/dist-cjs/index.d.ts +28 -8
- package/dist-cjs/index.js +2 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/ui/comment-composer.js +5 -0
- package/dist-cjs/ui/comment-composer.js.map +2 -2
- package/dist-cjs/ui/gesture-focus.js +37 -0
- package/dist-cjs/ui/gesture-focus.js.map +7 -0
- package/dist-cjs/ui/reaction-picker.js +5 -2
- package/dist-cjs/ui/reaction-picker.js.map +2 -2
- package/dist-cjs/ui/reaction.js +15 -5
- package/dist-cjs/ui/reaction.js.map +2 -2
- package/dist-cjs/ui/reply-count.js +8 -2
- package/dist-cjs/ui/reply-count.js.map +2 -2
- package/dist-esm/canvas/cluster-badge.mjs +8 -1
- package/dist-esm/canvas/cluster-badge.mjs.map +2 -2
- package/dist-esm/canvas/comment-reactions.mjs +12 -4
- package/dist-esm/canvas/comment-reactions.mjs.map +2 -2
- package/dist-esm/canvas/comment-render.mjs.map +2 -2
- package/dist-esm/canvas/comment-tool.mjs +37 -16
- package/dist-esm/canvas/comment-tool.mjs.map +2 -2
- package/dist-esm/canvas/comments-filter-menu.mjs +14 -6
- package/dist-esm/canvas/comments-filter-menu.mjs.map +2 -2
- package/dist-esm/canvas/comments-menu-item.mjs +5 -2
- package/dist-esm/canvas/comments-menu-item.mjs.map +2 -2
- package/dist-esm/canvas/comments-overflow-menu.mjs +10 -4
- package/dist-esm/canvas/comments-overflow-menu.mjs.map +2 -2
- package/dist-esm/canvas/comments-sidebar.mjs +21 -6
- package/dist-esm/canvas/comments-sidebar.mjs.map +2 -2
- package/dist-esm/canvas/comments-visibility-toggle.mjs +6 -2
- package/dist-esm/canvas/comments-visibility-toggle.mjs.map +2 -2
- package/dist-esm/canvas/options.mjs.map +1 -1
- package/dist-esm/canvas/pending-composer.mjs +8 -2
- package/dist-esm/canvas/pending-composer.mjs.map +2 -2
- package/dist-esm/canvas/thread-pin.mjs +15 -3
- package/dist-esm/canvas/thread-pin.mjs.map +2 -2
- package/dist-esm/canvas/thread-preview.mjs +5 -1
- package/dist-esm/canvas/thread-preview.mjs.map +2 -2
- package/dist-esm/canvas/thread-stack.mjs +19 -4
- package/dist-esm/canvas/thread-stack.mjs.map +2 -2
- package/dist-esm/canvas/thread-view.mjs +42 -21
- package/dist-esm/canvas/thread-view.mjs.map +2 -2
- package/dist-esm/index.d.mts +28 -8
- package/dist-esm/index.mjs +8 -2
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/ui/comment-composer.mjs +5 -0
- package/dist-esm/ui/comment-composer.mjs.map +2 -2
- package/dist-esm/ui/gesture-focus.mjs +17 -0
- package/dist-esm/ui/gesture-focus.mjs.map +7 -0
- package/dist-esm/ui/reaction-picker.mjs +6 -2
- package/dist-esm/ui/reaction-picker.mjs.map +2 -2
- package/dist-esm/ui/reaction.mjs +16 -8
- package/dist-esm/ui/reaction.mjs.map +2 -2
- package/dist-esm/ui/reply-count.mjs +8 -2
- package/dist-esm/ui/reply-count.mjs.map +2 -2
- package/package.json +5 -5
- package/src/canvas/cluster-badge.tsx +9 -1
- package/src/canvas/comment-reactions.tsx +18 -7
- package/src/canvas/comment-render.ts +6 -4
- package/src/canvas/comment-tool.test.ts +83 -7
- package/src/canvas/comment-tool.tsx +61 -19
- package/src/canvas/comments-filter-menu.tsx +15 -6
- package/src/canvas/comments-menu-item.tsx +6 -2
- package/src/canvas/comments-overflow-menu.tsx +11 -4
- package/src/canvas/comments-sidebar.tsx +23 -6
- package/src/canvas/comments-visibility-toggle.tsx +7 -2
- package/src/canvas/options.ts +1 -1
- package/src/canvas/pending-composer.tsx +11 -2
- package/src/canvas/thread-pin.tsx +17 -4
- package/src/canvas/thread-preview.tsx +6 -1
- package/src/canvas/thread-stack.tsx +21 -4
- package/src/canvas/thread-view.tsx +44 -22
- package/src/index.ts +6 -1
- package/src/ui/comment-composer.tsx +9 -0
- package/src/ui/gesture-focus.test.ts +45 -0
- package/src/ui/gesture-focus.ts +29 -0
- package/src/ui/reaction-picker.tsx +7 -2
- package/src/ui/reaction.test.tsx +22 -0
- package/src/ui/reaction.tsx +25 -14
- package/src/ui/reply-count.ts +9 -3
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
-
StateNode
|
|
2
|
+
StateNode,
|
|
3
|
+
defineMessages
|
|
3
4
|
} from "tldraw";
|
|
4
5
|
import { defaultCommentingOptions, getCommentingOptions } from "./options.mjs";
|
|
5
6
|
import { commentsSidebarOpen, pendingComment, regionDraft } from "./state.mjs";
|
|
@@ -60,6 +61,26 @@ class CommentTool extends StateNode {
|
|
|
60
61
|
this.editor.setCurrentTool("select");
|
|
61
62
|
}
|
|
62
63
|
}
|
|
64
|
+
function pendingCommentAt(editor, point) {
|
|
65
|
+
const hit = commentTargetShapeAt(editor, point);
|
|
66
|
+
const anchor = hit ? shapeAnchorAt(
|
|
67
|
+
editor,
|
|
68
|
+
hit.id,
|
|
69
|
+
point,
|
|
70
|
+
getCommentingOptions(editor).shouldBePrecise(editor, {
|
|
71
|
+
shapeId: hit.id,
|
|
72
|
+
point,
|
|
73
|
+
altKey: editor.inputs.getAltKey()
|
|
74
|
+
})
|
|
75
|
+
) : { type: "point", x: point.x, y: point.y };
|
|
76
|
+
return { anchor, point: { x: point.x, y: point.y } };
|
|
77
|
+
}
|
|
78
|
+
function startCommentAt(editor, point) {
|
|
79
|
+
editor.setCurrentTool("comment");
|
|
80
|
+
const pending = pendingCommentAt(editor, point);
|
|
81
|
+
pendingComment.set(editor, pending);
|
|
82
|
+
editor.setHintingShapes(pending.anchor.type === "shape" ? [pending.anchor.shapeId] : []);
|
|
83
|
+
}
|
|
63
84
|
function updateAnchorHint(editor) {
|
|
64
85
|
const hit = commentTargetShapeAt(editor, editor.inputs.getCurrentPagePoint());
|
|
65
86
|
editor.setHintingShapes(hit ? [hit.id] : []);
|
|
@@ -78,6 +99,10 @@ class CommentIdle extends StateNode {
|
|
|
78
99
|
updateAnchorHint(this.editor);
|
|
79
100
|
}
|
|
80
101
|
onPointerDown() {
|
|
102
|
+
if (pendingComment.get(this.editor)) {
|
|
103
|
+
this.editor.setCurrentTool("select");
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
81
106
|
this.parent.transition("pointing");
|
|
82
107
|
}
|
|
83
108
|
}
|
|
@@ -106,18 +131,7 @@ class CommentPointing extends StateNode {
|
|
|
106
131
|
onPointerUp() {
|
|
107
132
|
const { editor } = this;
|
|
108
133
|
const point = editor.inputs.getCurrentPagePoint();
|
|
109
|
-
|
|
110
|
-
const anchor = hit ? shapeAnchorAt(
|
|
111
|
-
editor,
|
|
112
|
-
hit.id,
|
|
113
|
-
point,
|
|
114
|
-
getCommentingOptions(editor).shouldBePrecise(editor, {
|
|
115
|
-
shapeId: hit.id,
|
|
116
|
-
point,
|
|
117
|
-
altKey: editor.inputs.getAltKey()
|
|
118
|
-
})
|
|
119
|
-
) : { type: "point", x: point.x, y: point.y };
|
|
120
|
-
pendingComment.set(editor, { anchor, point: { x: point.x, y: point.y } });
|
|
134
|
+
pendingComment.set(editor, pendingCommentAt(editor, point));
|
|
121
135
|
this.parent.transition("idle");
|
|
122
136
|
}
|
|
123
137
|
onCancel() {
|
|
@@ -176,14 +190,20 @@ class CommentDragging extends StateNode {
|
|
|
176
190
|
}
|
|
177
191
|
}
|
|
178
192
|
const commentTools = [CommentTool];
|
|
193
|
+
const messages = defineMessages({
|
|
194
|
+
toolComment: { id: "tool.comment", defaultMessage: "Comment" }
|
|
195
|
+
});
|
|
179
196
|
const commentToolOverrides = {
|
|
180
197
|
tools(editor, tools) {
|
|
181
198
|
tools.comment = {
|
|
182
199
|
id: "comment",
|
|
183
200
|
icon: "comment",
|
|
184
|
-
label:
|
|
201
|
+
label: messages.toolComment.id,
|
|
185
202
|
kbd: "c",
|
|
186
|
-
onSelect: () =>
|
|
203
|
+
onSelect: (source) => {
|
|
204
|
+
if (source === "context-menu") startCommentAt(editor, editor.inputs.getOriginPagePoint());
|
|
205
|
+
else editor.setCurrentTool("comment");
|
|
206
|
+
}
|
|
187
207
|
};
|
|
188
208
|
return tools;
|
|
189
209
|
}
|
|
@@ -192,6 +212,7 @@ export {
|
|
|
192
212
|
CommentTool,
|
|
193
213
|
commentToolOverrides,
|
|
194
214
|
commentTools,
|
|
195
|
-
regionBetween
|
|
215
|
+
regionBetween,
|
|
216
|
+
startCommentAt
|
|
196
217
|
};
|
|
197
218
|
//# sourceMappingURL=comment-tool.mjs.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/canvas/comment-tool.tsx"],
|
|
4
|
-
"sourcesContent": ["import {\n\tBoxModel,\n\tEditor,\n\tStateNode,\n\tTLCommentAnchor,\n\tTLStateNodeConstructor,\n\tTLUiOverrides,\n\tVecLike,\n} from 'tldraw'\nimport { type CommentingOptions, defaultCommentingOptions, getCommentingOptions } from './options'\nimport { commentsSidebarOpen, pendingComment, regionDraft } from './state'\nimport { commentTargetShapeAt, regionPinPoint, shapeAnchorAt } from './thread-state'\n\n/** A comment being placed but not yet posted: where its composer sits and what it will anchor\n * to. Shared between the tool (which sets it on click) and the overlay (which renders the\n * composer) through the internal `pendingComment` atom.\n * @internal */\nexport interface PendingComment {\n\tanchor: TLCommentAnchor\n\t/** Page point where the composer opens (the click location, or a region's pin corner). */\n\tpoint: VecLike\n}\n\n/**\n * Merge configure options over a base. Shallow for scalars; `components` is merged rather than\n * replaced so chained `configure` calls layer their slots \u2014 a later `{ components: { PinContent } }`\n * doesn't drop an earlier `{ components: { CommentBody } }`.\n */\nfunction mergeCommentingOptions(\n\tbase: CommentingOptions,\n\toverrides: Partial<CommentingOptions>\n): CommentingOptions {\n\treturn {\n\t\t...base,\n\t\t...overrides,\n\t\tcomponents: { ...base.components, ...overrides.components },\n\t}\n}\n\n/** The page-space rectangle spanned by two points, normalized so w/h are non-negative. Shared by\n * region creation (pointer-down \u2192 cursor) and corner resize (fixed corner \u2192 cursor). */\nexport function regionBetween(a: VecLike, b: VecLike): BoxModel {\n\treturn {\n\t\tx: Math.min(a.x, b.x),\n\t\ty: Math.min(a.y, b.y),\n\t\tw: Math.abs(a.x - b.x),\n\t\th: Math.abs(a.y - b.y),\n\t}\n}\n\n/**\n * The comment tool. Pressing down opens the comment composer at the pointer and it follows until\n * release \u2014 like placing a sticky note \u2014 settling on a point, or on a shape when released over one.\n * With region comments enabled, dragging past the threshold draws a region rectangle instead.\n * Placement only opens a composer; the records are created when the comment is posted. The tool\n * stays active while the composer is open \u2014 posting returns to select, and clicking elsewhere\n * re-places the composer.\n * @public\n */\nexport class CommentTool extends StateNode {\n\tstatic override id = 'comment'\n\tstatic override initial = 'idle'\n\tstatic override children(): TLStateNodeConstructor[] {\n\t\treturn [CommentIdle, CommentPointing, CommentDragging]\n\t}\n\n\t/**\n\t * Configure this tool's {@link CommentTool.options | `options`}, returning a configured subclass\n\t * to register via `tools`. Mirrors `ShapeUtil.configure`. Layers over any prior `configure`, so\n\t * calls can be chained.\n\t *\n\t * @example\n\t * ```tsx\n\t * <Tldraw tools={[CommentTool.configure({ history: 'ignore', enableClustering: false })]} />\n\t * ```\n\t */\n\tstatic configure<T extends TLStateNodeConstructor>(\n\t\tthis: T,\n\t\toptions: T extends new (...args: any[]) => { options: infer Options } ? Partial<Options> : never\n\t): T {\n\t\t// @ts-expect-error -- mirrors ShapeUtil.configure; extending `this` is sound at runtime.\n\t\treturn class extends this {\n\t\t\t// @ts-expect-error\n\t\t\toptions = mergeCommentingOptions(this.options, options)\n\t\t}\n\t}\n\n\t/**\n\t * The merged commenting options for this editor. Read from anywhere via\n\t * {@link getCommentingOptions}. Override with {@link CommentTool.configure}.\n\t */\n\toptions: CommentingOptions = defaultCommentingOptions\n\n\toverride onEnter() {\n\t\tthis.editor.setCursor({ type: 'comment', rotation: 0 })\n\t\t// Placing comments is canvas-focused \u2014 the thread list gets out of the way while the tool is\n\t\t// active. Reopened via its own control (a button), never by leaving the tool.\n\t\tcommentsSidebarOpen.set(this.editor, false)\n\t}\n\n\toverride onExit() {\n\t\t// Drop the hover hint painted while pointing at shapes (see CommentIdle). The cursor resets\n\t\t// when the next tool takes over. The draft composer and region draft belong to the tool, so\n\t\t// they leave with it; the draft's text survives in the comment draft store.\n\t\tthis.editor.setHintingShapes([])\n\t\tpendingComment.set(this.editor, null)\n\t\tregionDraft.set(this.editor, null)\n\t}\n\n\t// Escape leaves the tool, like the built-in tools (the editor dispatches `cancel` on Escape).\n\toverride onCancel() {\n\t\tthis.editor.setCurrentTool('select')\n\t}\n}\n\n/** Hint the shape a comment placed at the pointer would anchor to, using the same hit-test as the\n * anchor resolution on release. Hinting shapes render an indicator ungated by the active tool, so\n * this shows the select-style outline while the comment tool \u2014 not select \u2014 is active. */\nfunction updateAnchorHint(editor: Editor) {\n\tconst hit = commentTargetShapeAt(editor, editor.inputs.getCurrentPagePoint())\n\teditor.setHintingShapes(hit ? [hit.id] : [])\n}\n\nclass CommentIdle extends StateNode {\n\tstatic override id = 'idle'\n\n\toverride onEnter() {\n\t\t// Back to hovering: restore the pin cursor (a prior placing state may have hidden it).\n\t\tthis.editor.setCursor({ type: 'comment', rotation: 0 })\n\t\t// With a region composer open, a shape outline under its pin corner would imply a shape\n\t\t// anchor the region will never use \u2014 and it would stick, since moves over the composer\n\t\t// never reach the canvas. Point placements keep the hint: it previews the next click.\n\t\tif (pendingComment.get(this.editor)?.anchor.type === 'region') {\n\t\t\tthis.editor.setHintingShapes([])\n\t\t} else {\n\t\t\tupdateAnchorHint(this.editor)\n\t\t}\n\t}\n\n\toverride onPointerMove() {\n\t\tupdateAnchorHint(this.editor)\n\t}\n\n\toverride onPointerDown() {\n\t\tthis.parent.transition('pointing')\n\t}\n}\n\nclass CommentPointing extends StateNode {\n\tstatic override id = 'pointing'\n\n\toverride onEnter() {\n\t\t// Open the composer immediately at the press point so it's visible from pointer-down (not just\n\t\t// on release), and let it trail the pointer while dragging \u2014 like placing a sticky note. The\n\t\t// anchor is a bare point for now; it's resolved (shape or point) on pointer up.\n\t\tconst { editor } = this\n\t\t// Hide the cursor while placing: the draft composer is the pointer's stand-in now, so the pin\n\t\t// cursor sitting over it just reads as clutter.\n\t\teditor.setCursor({ type: 'none', rotation: 0 })\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tpendingComment.set(editor, {\n\t\t\tanchor: { type: 'point', x: point.x, y: point.y },\n\t\t\tpoint: { x: point.x, y: point.y },\n\t\t})\n\t}\n\n\toverride onPointerMove() {\n\t\tconst { editor } = this\n\t\t// Once the pointer passes the drag threshold with region comments enabled, this is a region,\n\t\t// not a follow \u2014 hand off to the region drag (which clears this composer and draws the box).\n\t\tif (getCommentingOptions(editor).enableRegions && editor.inputs.getIsDragging()) {\n\t\t\tthis.parent.transition('dragging')\n\t\t\treturn\n\t\t}\n\t\t// Otherwise the composer trails the pointer \u2014 keep hinting the shape a release here would\n\t\t// anchor to, like the idle hover does.\n\t\tupdateAnchorHint(editor)\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tpendingComment.update(editor, (p) => (p ? { ...p, point: { x: point.x, y: point.y } } : p))\n\t}\n\n\t// Settle where the pointer is released: anchor to the shape under it, or drop a point.\n\toverride onPointerUp() {\n\t\tconst { editor } = this\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tconst hit = commentTargetShapeAt(editor, point)\n\t\tconst anchor: TLCommentAnchor = hit\n\t\t\t? shapeAnchorAt(\n\t\t\t\t\teditor,\n\t\t\t\t\thit.id,\n\t\t\t\t\tpoint,\n\t\t\t\t\tgetCommentingOptions(editor).shouldBePrecise(editor, {\n\t\t\t\t\t\tshapeId: hit.id,\n\t\t\t\t\t\tpoint,\n\t\t\t\t\t\taltKey: editor.inputs.getAltKey(),\n\t\t\t\t\t})\n\t\t\t\t)\n\t\t\t: { type: 'point', x: point.x, y: point.y }\n\t\tpendingComment.set(editor, { anchor, point: { x: point.x, y: point.y } })\n\t\t// Stay in the tool while the composer is open \u2014 the interaction isn't over until the\n\t\t// comment is posted or dismissed, and staying keeps the surrounding UI (style panel,\n\t\t// sidebar) from churning mid-placement.\n\t\tthis.parent.transition('idle')\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onInterrupt() {\n\t\tthis.cancel()\n\t}\n\n\t// Abandon the follow composer if placement is interrupted (Escape, focus loss, etc.). The\n\t// tool's onExit drops the draft.\n\tprivate cancel() {\n\t\tthis.editor.setCurrentTool('select')\n\t}\n}\n\nclass CommentDragging extends StateNode {\n\tstatic override id = 'dragging'\n\n\toverride onEnter() {\n\t\t// A region drag supersedes the point-follow composer opened in `pointing`. Show a crosshair again,\n\t\t// since the composer no longer stands in for the pointer, and drop the placement hint \u2014 a region\n\t\t// anchors to its rectangle, so a single-shape outline would be stale.\n\t\tpendingComment.set(this.editor, null)\n\t\tthis.editor.setHintingShapes([])\n\t\tthis.editor.setCursor({ type: 'cross', rotation: 0 })\n\t\tthis.updateDraft()\n\t}\n\n\toverride onPointerMove() {\n\t\tthis.updateDraft()\n\t}\n\n\t// Commit the dragged rectangle as a region anchor; the composer opens at its pin corner.\n\toverride onPointerUp() {\n\t\tconst { editor } = this\n\t\tconst origin = editor.inputs.getOriginPagePoint()\n\t\tconst current = editor.inputs.getCurrentPagePoint()\n\t\tconst region = regionBetween(origin, current)\n\t\t// The pin lives on the corner the drag released on \u2014 drag up-left, pin top-left.\n\t\tconst pin = { x: current.x >= origin.x ? 1 : 0, y: current.y >= origin.y ? 1 : 0 }\n\t\tregionDraft.set(editor, null)\n\t\tpendingComment.set(editor, {\n\t\t\tanchor: { type: 'region', ...region, pinX: pin.x, pinY: pin.y },\n\t\t\tpoint: regionPinPoint(region, pin),\n\t\t})\n\t\t// Same as the point placement: the tool stays active while the composer is open.\n\t\tthis.parent.transition('idle')\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onInterrupt() {\n\t\tthis.cancel()\n\t}\n\n\tprivate updateDraft() {\n\t\tconst { editor } = this\n\t\tregionDraft.set(\n\t\t\teditor,\n\t\t\tregionBetween(editor.inputs.getOriginPagePoint(), editor.inputs.getCurrentPagePoint())\n\t\t)\n\t}\n\n\t// The tool's onExit drops the region draft.\n\tprivate cancel() {\n\t\tthis.editor.setCurrentTool('select')\n\t}\n}\n\n/** @public */\nexport const commentTools = [CommentTool]\n\n/** Registers the comment tool in the UI (icon, label, shortcut). Compose into your overrides.\n * Once registered, tldraw's `DefaultQuickActionsContent` shows the comment button.\n * @public */\nexport const commentToolOverrides: TLUiOverrides = {\n\ttools(editor, tools) {\n\t\ttools.comment = {\n\t\t\tid: 'comment',\n\t\t\ticon: 'comment',\n\t\t\tlabel: 'Comment',\n\t\t\tkbd: 'c',\n\t\t\tonSelect: () => editor.setCurrentTool('comment'),\n\t\t}\n\t\treturn tools\n\t},\n}\n"],
|
|
5
|
-
"mappings": "AAAA;AAAA,EAGC;AAAA,
|
|
4
|
+
"sourcesContent": ["import {\n\tBoxModel,\n\tEditor,\n\tStateNode,\n\tTLCommentAnchor,\n\tTLStateNodeConstructor,\n\tTLUiOverrides,\n\tVecLike,\n\tdefineMessages,\n} from 'tldraw'\nimport { type CommentingOptions, defaultCommentingOptions, getCommentingOptions } from './options'\nimport { commentsSidebarOpen, pendingComment, regionDraft } from './state'\nimport { commentTargetShapeAt, regionPinPoint, shapeAnchorAt } from './thread-state'\n\n/** A comment being placed but not yet posted: where its composer sits and what it will anchor\n * to. Shared between the tool (which sets it on click) and the overlay (which renders the\n * composer) through the internal `pendingComment` atom.\n * @internal */\nexport interface PendingComment {\n\tanchor: TLCommentAnchor\n\t/** Page point where the composer opens (the click location, or a region's pin corner). */\n\tpoint: VecLike\n}\n\n/**\n * Merge configure options over a base. Shallow for scalars; `components` is merged rather than\n * replaced so chained `configure` calls layer their slots \u2014 a later `{ components: { PinContent } }`\n * doesn't drop an earlier `{ components: { CommentBody } }`.\n */\nfunction mergeCommentingOptions(\n\tbase: CommentingOptions,\n\toverrides: Partial<CommentingOptions>\n): CommentingOptions {\n\treturn {\n\t\t...base,\n\t\t...overrides,\n\t\tcomponents: { ...base.components, ...overrides.components },\n\t}\n}\n\n/** The page-space rectangle spanned by two points, normalized so w/h are non-negative. Shared by\n * region creation (pointer-down \u2192 cursor) and corner resize (fixed corner \u2192 cursor). */\nexport function regionBetween(a: VecLike, b: VecLike): BoxModel {\n\treturn {\n\t\tx: Math.min(a.x, b.x),\n\t\ty: Math.min(a.y, b.y),\n\t\tw: Math.abs(a.x - b.x),\n\t\th: Math.abs(a.y - b.y),\n\t}\n}\n\n/**\n * The comment tool. Pressing down opens the comment composer at the pointer and it follows until\n * release \u2014 like placing a sticky note \u2014 settling on a point, or on a shape when released over one.\n * With region comments enabled, dragging past the threshold draws a region rectangle instead.\n * Placement only opens a composer; the records are created when the comment is posted. The tool\n * stays active while the composer is open \u2014 posting returns to select, and so does clicking the\n * canvas elsewhere, which cancels the placement.\n * @public\n */\nexport class CommentTool extends StateNode {\n\tstatic override id = 'comment'\n\tstatic override initial = 'idle'\n\tstatic override children(): TLStateNodeConstructor[] {\n\t\treturn [CommentIdle, CommentPointing, CommentDragging]\n\t}\n\n\t/**\n\t * Configure this tool's {@link CommentTool.options | `options`}, returning a configured subclass\n\t * to register via `tools`. Mirrors `ShapeUtil.configure`. Layers over any prior `configure`, so\n\t * calls can be chained.\n\t *\n\t * @example\n\t * ```tsx\n\t * <Tldraw tools={[CommentTool.configure({ history: 'ignore', enableClustering: false })]} />\n\t * ```\n\t */\n\tstatic configure<T extends TLStateNodeConstructor>(\n\t\tthis: T,\n\t\toptions: T extends new (...args: any[]) => { options: infer Options } ? Partial<Options> : never\n\t): T {\n\t\t// @ts-expect-error -- mirrors ShapeUtil.configure; extending `this` is sound at runtime.\n\t\treturn class extends this {\n\t\t\t// @ts-expect-error\n\t\t\toptions = mergeCommentingOptions(this.options, options)\n\t\t}\n\t}\n\n\t/**\n\t * The merged commenting options for this editor. Read from anywhere via\n\t * {@link getCommentingOptions}. Override with {@link CommentTool.configure}.\n\t */\n\toptions: CommentingOptions = defaultCommentingOptions\n\n\toverride onEnter() {\n\t\tthis.editor.setCursor({ type: 'comment', rotation: 0 })\n\t\t// Placing comments is canvas-focused \u2014 the thread list gets out of the way while the tool is\n\t\t// active. Reopened via its own control (a button), never by leaving the tool.\n\t\tcommentsSidebarOpen.set(this.editor, false)\n\t}\n\n\toverride onExit() {\n\t\t// Drop the hover hint painted while pointing at shapes (see CommentIdle). The cursor resets\n\t\t// when the next tool takes over. The draft composer and region draft belong to the tool, so\n\t\t// they leave with it; the draft's text survives in the comment draft store.\n\t\tthis.editor.setHintingShapes([])\n\t\tpendingComment.set(this.editor, null)\n\t\tregionDraft.set(this.editor, null)\n\t}\n\n\t// Escape leaves the tool, like the built-in tools (the editor dispatches `cancel` on Escape).\n\toverride onCancel() {\n\t\tthis.editor.setCurrentTool('select')\n\t}\n}\n\nfunction pendingCommentAt(editor: Editor, point: VecLike): PendingComment {\n\tconst hit = commentTargetShapeAt(editor, point)\n\tconst anchor: TLCommentAnchor = hit\n\t\t? shapeAnchorAt(\n\t\t\t\teditor,\n\t\t\t\thit.id,\n\t\t\t\tpoint,\n\t\t\t\tgetCommentingOptions(editor).shouldBePrecise(editor, {\n\t\t\t\t\tshapeId: hit.id,\n\t\t\t\t\tpoint,\n\t\t\t\t\taltKey: editor.inputs.getAltKey(),\n\t\t\t\t})\n\t\t\t)\n\t\t: { type: 'point', x: point.x, y: point.y }\n\treturn { anchor, point: { x: point.x, y: point.y } }\n}\n\n/**\n * Open the comment composer at a page point, anchored as a comment tool click there would be. Use\n * it to start a comment from outside the tool, such as a context menu item. Enters the comment tool\n * (which must be registered), since the draft composer closes when the tool exits. It doesn't check\n * `canComment`: a viewer who can't comment sees the `ComposerFallback` slot instead.\n *\n * @example\n * ```tsx\n * <TldrawUiMenuItem id=\"comment\" label=\"Comment\" onSelect={() => startCommentAt(editor, point)} />\n * ```\n * @public\n */\nexport function startCommentAt(editor: Editor, point: VecLike) {\n\teditor.setCurrentTool('comment')\n\tconst pending = pendingCommentAt(editor, point)\n\tpendingComment.set(editor, pending)\n\t// Entering the tool hints the shape under the live pointer, which is wherever the caller's UI\n\t// was (a menu item), not `point`.\n\teditor.setHintingShapes(pending.anchor.type === 'shape' ? [pending.anchor.shapeId] : [])\n}\n\n/** Hint the shape a comment placed at the pointer would anchor to, using the same hit-test as the\n * anchor resolution on release. Hinting shapes render an indicator ungated by the active tool, so\n * this shows the select-style outline while the comment tool \u2014 not select \u2014 is active. */\nfunction updateAnchorHint(editor: Editor) {\n\tconst hit = commentTargetShapeAt(editor, editor.inputs.getCurrentPagePoint())\n\teditor.setHintingShapes(hit ? [hit.id] : [])\n}\n\nclass CommentIdle extends StateNode {\n\tstatic override id = 'idle'\n\n\toverride onEnter() {\n\t\t// Back to hovering: restore the pin cursor (a prior placing state may have hidden it).\n\t\tthis.editor.setCursor({ type: 'comment', rotation: 0 })\n\t\t// With a region composer open, a shape outline under its pin corner would imply a shape\n\t\t// anchor the region will never use \u2014 and it would stick, since moves over the composer\n\t\t// never reach the canvas. Point placements keep the hint: it previews the next click.\n\t\tif (pendingComment.get(this.editor)?.anchor.type === 'region') {\n\t\t\tthis.editor.setHintingShapes([])\n\t\t} else {\n\t\t\tupdateAnchorHint(this.editor)\n\t\t}\n\t}\n\n\toverride onPointerMove() {\n\t\tupdateAnchorHint(this.editor)\n\t}\n\n\toverride onPointerDown() {\n\t\t// A press away from an open composer cancels the placement rather than moving it. The\n\t\t// tool's onExit drops the draft composer; its text survives in the draft store.\n\t\tif (pendingComment.get(this.editor)) {\n\t\t\tthis.editor.setCurrentTool('select')\n\t\t\treturn\n\t\t}\n\t\tthis.parent.transition('pointing')\n\t}\n}\n\nclass CommentPointing extends StateNode {\n\tstatic override id = 'pointing'\n\n\toverride onEnter() {\n\t\t// Open the composer immediately at the press point so it's visible from pointer-down (not just\n\t\t// on release), and let it trail the pointer while dragging \u2014 like placing a sticky note. The\n\t\t// anchor is a bare point for now; it's resolved (shape or point) on pointer up.\n\t\tconst { editor } = this\n\t\t// Hide the cursor while placing: the draft composer is the pointer's stand-in now, so the pin\n\t\t// cursor sitting over it just reads as clutter.\n\t\teditor.setCursor({ type: 'none', rotation: 0 })\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tpendingComment.set(editor, {\n\t\t\tanchor: { type: 'point', x: point.x, y: point.y },\n\t\t\tpoint: { x: point.x, y: point.y },\n\t\t})\n\t}\n\n\toverride onPointerMove() {\n\t\tconst { editor } = this\n\t\t// Once the pointer passes the drag threshold with region comments enabled, this is a region,\n\t\t// not a follow \u2014 hand off to the region drag (which clears this composer and draws the box).\n\t\tif (getCommentingOptions(editor).enableRegions && editor.inputs.getIsDragging()) {\n\t\t\tthis.parent.transition('dragging')\n\t\t\treturn\n\t\t}\n\t\t// Otherwise the composer trails the pointer \u2014 keep hinting the shape a release here would\n\t\t// anchor to, like the idle hover does.\n\t\tupdateAnchorHint(editor)\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tpendingComment.update(editor, (p) => (p ? { ...p, point: { x: point.x, y: point.y } } : p))\n\t}\n\n\t// Settle where the pointer is released: anchor to the shape under it, or drop a point.\n\toverride onPointerUp() {\n\t\tconst { editor } = this\n\t\tconst point = editor.inputs.getCurrentPagePoint()\n\t\tpendingComment.set(editor, pendingCommentAt(editor, point))\n\t\t// Stay in the tool while the composer is open \u2014 the interaction isn't over until the\n\t\t// comment is posted or dismissed, and staying keeps the surrounding UI (style panel,\n\t\t// sidebar) from churning mid-placement.\n\t\tthis.parent.transition('idle')\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onInterrupt() {\n\t\tthis.cancel()\n\t}\n\n\t// Abandon the follow composer if placement is interrupted (Escape, focus loss, etc.). The\n\t// tool's onExit drops the draft.\n\tprivate cancel() {\n\t\tthis.editor.setCurrentTool('select')\n\t}\n}\n\nclass CommentDragging extends StateNode {\n\tstatic override id = 'dragging'\n\n\toverride onEnter() {\n\t\t// A region drag supersedes the point-follow composer opened in `pointing`. Show a crosshair again,\n\t\t// since the composer no longer stands in for the pointer, and drop the placement hint \u2014 a region\n\t\t// anchors to its rectangle, so a single-shape outline would be stale.\n\t\tpendingComment.set(this.editor, null)\n\t\tthis.editor.setHintingShapes([])\n\t\tthis.editor.setCursor({ type: 'cross', rotation: 0 })\n\t\tthis.updateDraft()\n\t}\n\n\toverride onPointerMove() {\n\t\tthis.updateDraft()\n\t}\n\n\t// Commit the dragged rectangle as a region anchor; the composer opens at its pin corner.\n\toverride onPointerUp() {\n\t\tconst { editor } = this\n\t\tconst origin = editor.inputs.getOriginPagePoint()\n\t\tconst current = editor.inputs.getCurrentPagePoint()\n\t\tconst region = regionBetween(origin, current)\n\t\t// The pin lives on the corner the drag released on \u2014 drag up-left, pin top-left.\n\t\tconst pin = { x: current.x >= origin.x ? 1 : 0, y: current.y >= origin.y ? 1 : 0 }\n\t\tregionDraft.set(editor, null)\n\t\tpendingComment.set(editor, {\n\t\t\tanchor: { type: 'region', ...region, pinX: pin.x, pinY: pin.y },\n\t\t\tpoint: regionPinPoint(region, pin),\n\t\t})\n\t\t// Same as the point placement: the tool stays active while the composer is open.\n\t\tthis.parent.transition('idle')\n\t}\n\n\toverride onCancel() {\n\t\tthis.cancel()\n\t}\n\n\toverride onInterrupt() {\n\t\tthis.cancel()\n\t}\n\n\tprivate updateDraft() {\n\t\tconst { editor } = this\n\t\tregionDraft.set(\n\t\t\teditor,\n\t\t\tregionBetween(editor.inputs.getOriginPagePoint(), editor.inputs.getCurrentPagePoint())\n\t\t)\n\t}\n\n\t// The tool's onExit drops the region draft.\n\tprivate cancel() {\n\t\tthis.editor.setCurrentTool('select')\n\t}\n}\n\n/** @public */\nexport const commentTools = [CommentTool]\n\nconst messages = defineMessages({\n\ttoolComment: { id: 'tool.comment', defaultMessage: 'Comment' },\n})\n\n/** Registers the comment tool in the UI (icon, label, shortcut). Compose into your overrides.\n * Once registered, tldraw's `DefaultQuickActionsContent` shows the comment button, and its\n * `DefaultContextMenuContent` shows a comment item that opens the composer where the menu was\n * opened.\n * @public */\nexport const commentToolOverrides: TLUiOverrides = {\n\ttools(editor, tools) {\n\t\ttools.comment = {\n\t\t\tid: 'comment',\n\t\t\ticon: 'comment',\n\t\t\tlabel: messages.toolComment.id,\n\t\t\tkbd: 'c',\n\t\t\tonSelect: (source) => {\n\t\t\t\t// The menu was opened by the last pointer down; the live pointer is over the menu now.\n\t\t\t\tif (source === 'context-menu') startCommentAt(editor, editor.inputs.getOriginPagePoint())\n\t\t\t\telse editor.setCurrentTool('comment')\n\t\t\t},\n\t\t}\n\t\treturn tools\n\t},\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import {
|
|
3
|
+
defineMessages,
|
|
3
4
|
TldrawUiButton,
|
|
4
5
|
TldrawUiDropdownMenuContent,
|
|
5
6
|
TldrawUiDropdownMenuRoot,
|
|
@@ -13,6 +14,13 @@ import {
|
|
|
13
14
|
} from "tldraw";
|
|
14
15
|
import { MoreMenuIcon } from "../ui/icons.mjs";
|
|
15
16
|
import { sidebarFilters } from "./state.mjs";
|
|
17
|
+
const messages = defineMessages({
|
|
18
|
+
commentsFilter: { id: "comments.filter", defaultMessage: "Filter comments" },
|
|
19
|
+
commentsOnlyMyComments: { id: "comments.only-my-comments", defaultMessage: "Only my comments" },
|
|
20
|
+
commentsOnlyUnread: { id: "comments.only-unread", defaultMessage: "Only unread" },
|
|
21
|
+
commentsShowAllPages: { id: "comments.show-all-pages", defaultMessage: "Show all pages" },
|
|
22
|
+
commentsShowResolved: { id: "comments.show-resolved", defaultMessage: "Show resolved comments" }
|
|
23
|
+
});
|
|
16
24
|
function CommentsFilterMenu({
|
|
17
25
|
canFilterByAuthor,
|
|
18
26
|
canFilterByUnread
|
|
@@ -28,8 +36,8 @@ function CommentsFilterMenu({
|
|
|
28
36
|
TldrawUiButton,
|
|
29
37
|
{
|
|
30
38
|
type: "icon",
|
|
31
|
-
tooltip: msg(
|
|
32
|
-
title: msg(
|
|
39
|
+
tooltip: msg(messages.commentsFilter),
|
|
40
|
+
title: msg(messages.commentsFilter),
|
|
33
41
|
className: "tlui-cmt-header-btn",
|
|
34
42
|
children: /* @__PURE__ */ jsx(MoreMenuIcon, {})
|
|
35
43
|
}
|
|
@@ -39,7 +47,7 @@ function CommentsFilterMenu({
|
|
|
39
47
|
TldrawUiMenuCheckboxItem,
|
|
40
48
|
{
|
|
41
49
|
id: "show-all-pages",
|
|
42
|
-
label:
|
|
50
|
+
label: messages.commentsShowAllPages.id,
|
|
43
51
|
checked: !filters.onlyCurrentPage,
|
|
44
52
|
onSelect: () => toggle("onlyCurrentPage")
|
|
45
53
|
}
|
|
@@ -48,7 +56,7 @@ function CommentsFilterMenu({
|
|
|
48
56
|
TldrawUiMenuCheckboxItem,
|
|
49
57
|
{
|
|
50
58
|
id: "only-my-comments",
|
|
51
|
-
label:
|
|
59
|
+
label: messages.commentsOnlyMyComments.id,
|
|
52
60
|
checked: filters.onlyMine,
|
|
53
61
|
onSelect: () => toggle("onlyMine")
|
|
54
62
|
}
|
|
@@ -57,7 +65,7 @@ function CommentsFilterMenu({
|
|
|
57
65
|
TldrawUiMenuCheckboxItem,
|
|
58
66
|
{
|
|
59
67
|
id: "only-unread",
|
|
60
|
-
label:
|
|
68
|
+
label: messages.commentsOnlyUnread.id,
|
|
61
69
|
checked: filters.onlyUnread,
|
|
62
70
|
onSelect: () => toggle("onlyUnread")
|
|
63
71
|
}
|
|
@@ -66,7 +74,7 @@ function CommentsFilterMenu({
|
|
|
66
74
|
TldrawUiMenuCheckboxItem,
|
|
67
75
|
{
|
|
68
76
|
id: "show-resolved",
|
|
69
|
-
label:
|
|
77
|
+
label: messages.commentsShowResolved.id,
|
|
70
78
|
checked: filters.showResolved,
|
|
71
79
|
onSelect: () => toggle("showResolved")
|
|
72
80
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/canvas/comments-filter-menu.tsx"],
|
|
4
|
-
"sourcesContent": ["import {\n\tTldrawUiButton,\n\tTldrawUiDropdownMenuContent,\n\tTldrawUiDropdownMenuRoot,\n\tTldrawUiDropdownMenuTrigger,\n\tTldrawUiMenuCheckboxItem,\n\tTldrawUiMenuContextProvider,\n\tTldrawUiMenuGroup,\n\tuseEditor,\n\tuseTranslation,\n\tuseValue,\n} from 'tldraw'\nimport { MoreMenuIcon } from '../ui/icons'\nimport { SidebarFilters } from './sidebar-filters'\nimport { sidebarFilters } from './state'\n\n/** @public */\nexport interface CommentsFilterMenuProps {\n\t/** Whether to offer the \"only your threads\" toggle (needs a known current user). */\n\tcanFilterByAuthor?: boolean\n\t/** Whether to offer the \"only unread\" toggle (needs a read-status source). */\n\tcanFilterByUnread?: boolean\n}\n\n/** The funnel dropdown in the sidebar header: toggles for which threads the list shows.\n * @public @react */\nexport function CommentsFilterMenu({\n\tcanFilterByAuthor,\n\tcanFilterByUnread,\n}: CommentsFilterMenuProps) {\n\tconst editor = useEditor()\n\tconst msg = useTranslation()\n\tconst filters = useValue('sidebar filters', () => sidebarFilters.get(editor), [editor])\n\tconst toggle = (key: keyof SidebarFilters) => {\n\t\tsidebarFilters.update(editor, (f) => ({ ...f, [key]: !f[key] }))\n\t}\n\n\treturn (\n\t\t<TldrawUiDropdownMenuRoot id=\"comments-filter\">\n\t\t\t<TldrawUiDropdownMenuTrigger>\n\t\t\t\t<TldrawUiButton\n\t\t\t\t\ttype=\"icon\"\n\t\t\t\t\ttooltip={msg(
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import {\n\tdefineMessages,\n\tTldrawUiButton,\n\tTldrawUiDropdownMenuContent,\n\tTldrawUiDropdownMenuRoot,\n\tTldrawUiDropdownMenuTrigger,\n\tTldrawUiMenuCheckboxItem,\n\tTldrawUiMenuContextProvider,\n\tTldrawUiMenuGroup,\n\tuseEditor,\n\tuseTranslation,\n\tuseValue,\n} from 'tldraw'\nimport { MoreMenuIcon } from '../ui/icons'\nimport { SidebarFilters } from './sidebar-filters'\nimport { sidebarFilters } from './state'\n\nconst messages = defineMessages({\n\tcommentsFilter: { id: 'comments.filter', defaultMessage: 'Filter comments' },\n\tcommentsOnlyMyComments: { id: 'comments.only-my-comments', defaultMessage: 'Only my comments' },\n\tcommentsOnlyUnread: { id: 'comments.only-unread', defaultMessage: 'Only unread' },\n\tcommentsShowAllPages: { id: 'comments.show-all-pages', defaultMessage: 'Show all pages' },\n\tcommentsShowResolved: { id: 'comments.show-resolved', defaultMessage: 'Show resolved comments' },\n})\n\n/** @public */\nexport interface CommentsFilterMenuProps {\n\t/** Whether to offer the \"only your threads\" toggle (needs a known current user). */\n\tcanFilterByAuthor?: boolean\n\t/** Whether to offer the \"only unread\" toggle (needs a read-status source). */\n\tcanFilterByUnread?: boolean\n}\n\n/** The funnel dropdown in the sidebar header: toggles for which threads the list shows.\n * @public @react */\nexport function CommentsFilterMenu({\n\tcanFilterByAuthor,\n\tcanFilterByUnread,\n}: CommentsFilterMenuProps) {\n\tconst editor = useEditor()\n\tconst msg = useTranslation()\n\tconst filters = useValue('sidebar filters', () => sidebarFilters.get(editor), [editor])\n\tconst toggle = (key: keyof SidebarFilters) => {\n\t\tsidebarFilters.update(editor, (f) => ({ ...f, [key]: !f[key] }))\n\t}\n\n\treturn (\n\t\t<TldrawUiDropdownMenuRoot id=\"comments-filter\">\n\t\t\t<TldrawUiDropdownMenuTrigger>\n\t\t\t\t<TldrawUiButton\n\t\t\t\t\ttype=\"icon\"\n\t\t\t\t\ttooltip={msg(messages.commentsFilter)}\n\t\t\t\t\ttitle={msg(messages.commentsFilter)}\n\t\t\t\t\tclassName=\"tlui-cmt-header-btn\"\n\t\t\t\t>\n\t\t\t\t\t<MoreMenuIcon />\n\t\t\t\t</TldrawUiButton>\n\t\t\t</TldrawUiDropdownMenuTrigger>\n\t\t\t{/* No tlui-cmt-menu here: the rows are tldraw menu items, which carry their own insets \u2014\n\t\t\t the extra padding would set this menu apart from the canvas menus it should match. */}\n\t\t\t<TldrawUiDropdownMenuContent side=\"bottom\" align=\"end\" alignOffset={0}>\n\t\t\t\t<TldrawUiMenuContextProvider type=\"menu\" sourceId=\"menu\">\n\t\t\t\t\t<TldrawUiMenuGroup id=\"comments-filter\">\n\t\t\t\t\t\t<TldrawUiMenuCheckboxItem\n\t\t\t\t\t\t\tid=\"show-all-pages\"\n\t\t\t\t\t\t\tlabel={messages.commentsShowAllPages.id}\n\t\t\t\t\t\t\tchecked={!filters.onlyCurrentPage}\n\t\t\t\t\t\t\tonSelect={() => toggle('onlyCurrentPage')}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t{canFilterByAuthor && (\n\t\t\t\t\t\t\t<TldrawUiMenuCheckboxItem\n\t\t\t\t\t\t\t\tid=\"only-my-comments\"\n\t\t\t\t\t\t\t\tlabel={messages.commentsOnlyMyComments.id}\n\t\t\t\t\t\t\t\tchecked={filters.onlyMine}\n\t\t\t\t\t\t\t\tonSelect={() => toggle('onlyMine')}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{canFilterByUnread && (\n\t\t\t\t\t\t\t<TldrawUiMenuCheckboxItem\n\t\t\t\t\t\t\t\tid=\"only-unread\"\n\t\t\t\t\t\t\t\tlabel={messages.commentsOnlyUnread.id}\n\t\t\t\t\t\t\t\tchecked={filters.onlyUnread}\n\t\t\t\t\t\t\t\tonSelect={() => toggle('onlyUnread')}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t<TldrawUiMenuCheckboxItem\n\t\t\t\t\t\t\tid=\"show-resolved\"\n\t\t\t\t\t\t\tlabel={messages.commentsShowResolved.id}\n\t\t\t\t\t\t\tchecked={filters.showResolved}\n\t\t\t\t\t\t\tonSelect={() => toggle('showResolved')}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</TldrawUiMenuGroup>\n\t\t\t\t</TldrawUiMenuContextProvider>\n\t\t\t</TldrawUiDropdownMenuContent>\n\t\t</TldrawUiDropdownMenuRoot>\n\t)\n}\n"],
|
|
5
|
+
"mappings": "AAuDK,cAOA,YAPA;AAvDL;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,oBAAoB;AAE7B,SAAS,sBAAsB;AAE/B,MAAM,WAAW,eAAe;AAAA,EAC/B,gBAAgB,EAAE,IAAI,mBAAmB,gBAAgB,kBAAkB;AAAA,EAC3E,wBAAwB,EAAE,IAAI,6BAA6B,gBAAgB,mBAAmB;AAAA,EAC9F,oBAAoB,EAAE,IAAI,wBAAwB,gBAAgB,cAAc;AAAA,EAChF,sBAAsB,EAAE,IAAI,2BAA2B,gBAAgB,iBAAiB;AAAA,EACxF,sBAAsB,EAAE,IAAI,0BAA0B,gBAAgB,yBAAyB;AAChG,CAAC;AAYM,SAAS,mBAAmB;AAAA,EAClC;AAAA,EACA;AACD,GAA4B;AAC3B,QAAM,SAAS,UAAU;AACzB,QAAM,MAAM,eAAe;AAC3B,QAAM,UAAU,SAAS,mBAAmB,MAAM,eAAe,IAAI,MAAM,GAAG,CAAC,MAAM,CAAC;AACtF,QAAM,SAAS,CAAC,QAA8B;AAC7C,mBAAe,OAAO,QAAQ,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,EAAE;AAAA,EAChE;AAEA,SACC,qBAAC,4BAAyB,IAAG,mBAC5B;AAAA,wBAAC,+BACA;AAAA,MAAC;AAAA;AAAA,QACA,MAAK;AAAA,QACL,SAAS,IAAI,SAAS,cAAc;AAAA,QACpC,OAAO,IAAI,SAAS,cAAc;AAAA,QAClC,WAAU;AAAA,QAEV,8BAAC,gBAAa;AAAA;AAAA,IACf,GACD;AAAA,IAGA,oBAAC,+BAA4B,MAAK,UAAS,OAAM,OAAM,aAAa,GACnE,8BAAC,+BAA4B,MAAK,QAAO,UAAS,QACjD,+BAAC,qBAAkB,IAAG,mBACrB;AAAA;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAO,SAAS,qBAAqB;AAAA,UACrC,SAAS,CAAC,QAAQ;AAAA,UAClB,UAAU,MAAM,OAAO,iBAAiB;AAAA;AAAA,MACzC;AAAA,MACC,qBACA;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAO,SAAS,uBAAuB;AAAA,UACvC,SAAS,QAAQ;AAAA,UACjB,UAAU,MAAM,OAAO,UAAU;AAAA;AAAA,MAClC;AAAA,MAEA,qBACA;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAO,SAAS,mBAAmB;AAAA,UACnC,SAAS,QAAQ;AAAA,UACjB,UAAU,MAAM,OAAO,YAAY;AAAA;AAAA,MACpC;AAAA,MAED;AAAA,QAAC;AAAA;AAAA,UACA,IAAG;AAAA,UACH,OAAO,SAAS,qBAAqB;AAAA,UACrC,SAAS,QAAQ;AAAA,UACjB,UAAU,MAAM,OAAO,cAAc;AAAA;AAAA,MACtC;AAAA,OACD,GACD,GACD;AAAA,KACD;AAEF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import { TldrawUiMenuCheckboxItem, useEditor, useValue } from "tldraw";
|
|
2
|
+
import { defineMessages, TldrawUiMenuCheckboxItem, useEditor, useValue } from "tldraw";
|
|
3
3
|
import { commentsHidden, toggleCommentsHidden } from "./state.mjs";
|
|
4
|
+
const messages = defineMessages({
|
|
5
|
+
commentsTitle: { id: "comments.title", defaultMessage: "Comments" }
|
|
6
|
+
});
|
|
4
7
|
function CommentsMenuItem() {
|
|
5
8
|
const editor = useEditor();
|
|
6
9
|
const hidden = useValue("comments hidden", () => commentsHidden.get(editor), [editor]);
|
|
@@ -8,7 +11,7 @@ function CommentsMenuItem() {
|
|
|
8
11
|
TldrawUiMenuCheckboxItem,
|
|
9
12
|
{
|
|
10
13
|
id: "comments-visible",
|
|
11
|
-
label:
|
|
14
|
+
label: messages.commentsTitle.id,
|
|
12
15
|
kbd: "!c",
|
|
13
16
|
readonlyOk: true,
|
|
14
17
|
checked: !hidden,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/canvas/comments-menu-item.tsx"],
|
|
4
|
-
"sourcesContent": ["import { TldrawUiMenuCheckboxItem, useEditor, useValue } from 'tldraw'\nimport { commentsHidden, toggleCommentsHidden } from './state'\n\n/**\n * A checkbox menu item for a \"View\" menu that shows/hides comment pins on the canvas \u2014 checked\n * when comments are visible, like tldraw's own grid/snap toggles. Reads and toggles the shared\n * {@link commentsHidden} signal, so it stays in sync with the Shift+C shortcut and the sidebar\n * control. Drop it into whichever menu your app owns.\n * @public @react\n */\nexport function CommentsMenuItem() {\n\tconst editor = useEditor()\n\tconst hidden = useValue('comments hidden', () => commentsHidden.get(editor), [editor])\n\treturn (\n\t\t<TldrawUiMenuCheckboxItem\n\t\t\tid=\"comments-visible\"\n\t\t\tlabel
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import { defineMessages, TldrawUiMenuCheckboxItem, useEditor, useValue } from 'tldraw'\nimport { commentsHidden, toggleCommentsHidden } from './state'\n\nconst messages = defineMessages({\n\tcommentsTitle: { id: 'comments.title', defaultMessage: 'Comments' },\n})\n\n/**\n * A checkbox menu item for a \"View\" menu that shows/hides comment pins on the canvas \u2014 checked\n * when comments are visible, like tldraw's own grid/snap toggles. Reads and toggles the shared\n * {@link commentsHidden} signal, so it stays in sync with the Shift+C shortcut and the sidebar\n * control. Drop it into whichever menu your app owns.\n * @public @react\n */\nexport function CommentsMenuItem() {\n\tconst editor = useEditor()\n\tconst hidden = useValue('comments hidden', () => commentsHidden.get(editor), [editor])\n\treturn (\n\t\t<TldrawUiMenuCheckboxItem\n\t\t\tid=\"comments-visible\"\n\t\t\tlabel={messages.commentsTitle.id}\n\t\t\tkbd=\"!c\"\n\t\t\treadonlyOk\n\t\t\tchecked={!hidden}\n\t\t\tonSelect={() => toggleCommentsHidden(editor)}\n\t\t/>\n\t)\n}\n"],
|
|
5
|
+
"mappings": "AAkBE;AAlBF,SAAS,gBAAgB,0BAA0B,WAAW,gBAAgB;AAC9E,SAAS,gBAAgB,4BAA4B;AAErD,MAAM,WAAW,eAAe;AAAA,EAC/B,eAAe,EAAE,IAAI,kBAAkB,gBAAgB,WAAW;AACnE,CAAC;AASM,SAAS,mBAAmB;AAClC,QAAM,SAAS,UAAU;AACzB,QAAM,SAAS,SAAS,mBAAmB,MAAM,eAAe,IAAI,MAAM,GAAG,CAAC,MAAM,CAAC;AACrF,SACC;AAAA,IAAC;AAAA;AAAA,MACA,IAAG;AAAA,MACH,OAAO,SAAS,cAAc;AAAA,MAC9B,KAAI;AAAA,MACJ,YAAU;AAAA,MACV,SAAS,CAAC;AAAA,MACV,UAAU,MAAM,qBAAqB,MAAM;AAAA;AAAA,EAC5C;AAEF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import {
|
|
3
|
+
defineMessages,
|
|
3
4
|
TldrawUiButton,
|
|
4
5
|
TldrawUiDropdownMenuContent,
|
|
5
6
|
TldrawUiDropdownMenuGroup,
|
|
@@ -12,6 +13,11 @@ import {
|
|
|
12
13
|
useValue
|
|
13
14
|
} from "tldraw";
|
|
14
15
|
import { commentsHidden, toggleCommentsHidden } from "./state.mjs";
|
|
16
|
+
const messages = defineMessages({
|
|
17
|
+
commentsMoreOptions: { id: "comments.more-options", defaultMessage: "More options" },
|
|
18
|
+
commentsHide: { id: "comments.hide", defaultMessage: "Hide comments" },
|
|
19
|
+
commentsShow: { id: "comments.show", defaultMessage: "Show comments" }
|
|
20
|
+
});
|
|
15
21
|
const HIDE_SHORTCUT = "\u21E7C";
|
|
16
22
|
function CommentsOverflowMenu() {
|
|
17
23
|
const editor = useEditor();
|
|
@@ -22,10 +28,10 @@ function CommentsOverflowMenu() {
|
|
|
22
28
|
TldrawUiButton,
|
|
23
29
|
{
|
|
24
30
|
type: "icon",
|
|
25
|
-
tooltip: msg(
|
|
26
|
-
title: msg(
|
|
31
|
+
tooltip: msg(messages.commentsMoreOptions),
|
|
32
|
+
title: msg(messages.commentsMoreOptions),
|
|
27
33
|
className: "tlui-cmt-header-btn",
|
|
28
|
-
children: /* @__PURE__ */ jsx(TldrawUiIcon, { icon: "dots-vertical", label: msg(
|
|
34
|
+
children: /* @__PURE__ */ jsx(TldrawUiIcon, { icon: "dots-vertical", label: msg(messages.commentsMoreOptions), small: true })
|
|
29
35
|
}
|
|
30
36
|
) }),
|
|
31
37
|
/* @__PURE__ */ jsx(
|
|
@@ -43,7 +49,7 @@ function CommentsOverflowMenu() {
|
|
|
43
49
|
className: "tlui-cmt-menu-item",
|
|
44
50
|
onClick: () => toggleCommentsHidden(editor),
|
|
45
51
|
children: [
|
|
46
|
-
/* @__PURE__ */ jsx("span", { children: hidden ? msg(
|
|
52
|
+
/* @__PURE__ */ jsx("span", { children: hidden ? msg(messages.commentsShow) : msg(messages.commentsHide) }),
|
|
47
53
|
/* @__PURE__ */ jsx("span", { className: "tlui-cmt-menu-item__shortcut", children: HIDE_SHORTCUT })
|
|
48
54
|
]
|
|
49
55
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/canvas/comments-overflow-menu.tsx"],
|
|
4
|
-
"sourcesContent": ["import {\n\tTldrawUiButton,\n\tTldrawUiDropdownMenuContent,\n\tTldrawUiDropdownMenuGroup,\n\tTldrawUiDropdownMenuItem,\n\tTldrawUiDropdownMenuRoot,\n\tTldrawUiDropdownMenuTrigger,\n\tTldrawUiIcon,\n\tuseEditor,\n\tuseTranslation,\n\tuseValue,\n} from 'tldraw'\nimport { commentsHidden, toggleCommentsHidden } from './state'\n\n// A keyboard-shortcut glyph, not translatable copy \u2014 kept out of JSX as a constant.\nconst HIDE_SHORTCUT = '\u21E7C'\n\n/** The overflow (\u22EF) dropdown in the sidebar header. For now it holds the hide/show-comments\n * toggle; it's the home for later comment-wide controls (notifications, mark all as read).\n * @public @react */\nexport function CommentsOverflowMenu() {\n\tconst editor = useEditor()\n\tconst msg = useTranslation()\n\tconst hidden = useValue('comments hidden', () => commentsHidden.get(editor), [editor])\n\n\treturn (\n\t\t<TldrawUiDropdownMenuRoot id=\"comments-overflow\">\n\t\t\t<TldrawUiDropdownMenuTrigger>\n\t\t\t\t<TldrawUiButton\n\t\t\t\t\ttype=\"icon\"\n\t\t\t\t\ttooltip={msg(
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import {\n\tdefineMessages,\n\tTldrawUiButton,\n\tTldrawUiDropdownMenuContent,\n\tTldrawUiDropdownMenuGroup,\n\tTldrawUiDropdownMenuItem,\n\tTldrawUiDropdownMenuRoot,\n\tTldrawUiDropdownMenuTrigger,\n\tTldrawUiIcon,\n\tuseEditor,\n\tuseTranslation,\n\tuseValue,\n} from 'tldraw'\nimport { commentsHidden, toggleCommentsHidden } from './state'\n\nconst messages = defineMessages({\n\tcommentsMoreOptions: { id: 'comments.more-options', defaultMessage: 'More options' },\n\tcommentsHide: { id: 'comments.hide', defaultMessage: 'Hide comments' },\n\tcommentsShow: { id: 'comments.show', defaultMessage: 'Show comments' },\n})\n\n// A keyboard-shortcut glyph, not translatable copy \u2014 kept out of JSX as a constant.\nconst HIDE_SHORTCUT = '\u21E7C'\n\n/** The overflow (\u22EF) dropdown in the sidebar header. For now it holds the hide/show-comments\n * toggle; it's the home for later comment-wide controls (notifications, mark all as read).\n * @public @react */\nexport function CommentsOverflowMenu() {\n\tconst editor = useEditor()\n\tconst msg = useTranslation()\n\tconst hidden = useValue('comments hidden', () => commentsHidden.get(editor), [editor])\n\n\treturn (\n\t\t<TldrawUiDropdownMenuRoot id=\"comments-overflow\">\n\t\t\t<TldrawUiDropdownMenuTrigger>\n\t\t\t\t<TldrawUiButton\n\t\t\t\t\ttype=\"icon\"\n\t\t\t\t\ttooltip={msg(messages.commentsMoreOptions)}\n\t\t\t\t\ttitle={msg(messages.commentsMoreOptions)}\n\t\t\t\t\tclassName=\"tlui-cmt-header-btn\"\n\t\t\t\t>\n\t\t\t\t\t<TldrawUiIcon icon=\"dots-vertical\" label={msg(messages.commentsMoreOptions)} small />\n\t\t\t\t</TldrawUiButton>\n\t\t\t</TldrawUiDropdownMenuTrigger>\n\t\t\t<TldrawUiDropdownMenuContent\n\t\t\t\tclassName=\"tlui-cmt-menu\"\n\t\t\t\tside=\"bottom\"\n\t\t\t\talign=\"end\"\n\t\t\t\talignOffset={0}\n\t\t\t\tsideOffset={4}\n\t\t\t>\n\t\t\t\t<TldrawUiDropdownMenuGroup>\n\t\t\t\t\t<TldrawUiDropdownMenuItem>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclassName=\"tlui-cmt-menu-item\"\n\t\t\t\t\t\t\tonClick={() => toggleCommentsHidden(editor)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<span>{hidden ? msg(messages.commentsShow) : msg(messages.commentsHide)}</span>\n\t\t\t\t\t\t\t<span className=\"tlui-cmt-menu-item__shortcut\">{HIDE_SHORTCUT}</span>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</TldrawUiDropdownMenuItem>\n\t\t\t\t</TldrawUiDropdownMenuGroup>\n\t\t\t</TldrawUiDropdownMenuContent>\n\t\t</TldrawUiDropdownMenuRoot>\n\t)\n}\n"],
|
|
5
|
+
"mappings": "AAyCK,cAYC,YAZD;AAzCL;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AACP,SAAS,gBAAgB,4BAA4B;AAErD,MAAM,WAAW,eAAe;AAAA,EAC/B,qBAAqB,EAAE,IAAI,yBAAyB,gBAAgB,eAAe;AAAA,EACnF,cAAc,EAAE,IAAI,iBAAiB,gBAAgB,gBAAgB;AAAA,EACrE,cAAc,EAAE,IAAI,iBAAiB,gBAAgB,gBAAgB;AACtE,CAAC;AAGD,MAAM,gBAAgB;AAKf,SAAS,uBAAuB;AACtC,QAAM,SAAS,UAAU;AACzB,QAAM,MAAM,eAAe;AAC3B,QAAM,SAAS,SAAS,mBAAmB,MAAM,eAAe,IAAI,MAAM,GAAG,CAAC,MAAM,CAAC;AAErF,SACC,qBAAC,4BAAyB,IAAG,qBAC5B;AAAA,wBAAC,+BACA;AAAA,MAAC;AAAA;AAAA,QACA,MAAK;AAAA,QACL,SAAS,IAAI,SAAS,mBAAmB;AAAA,QACzC,OAAO,IAAI,SAAS,mBAAmB;AAAA,QACvC,WAAU;AAAA,QAEV,8BAAC,gBAAa,MAAK,iBAAgB,OAAO,IAAI,SAAS,mBAAmB,GAAG,OAAK,MAAC;AAAA;AAAA,IACpF,GACD;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACA,WAAU;AAAA,QACV,MAAK;AAAA,QACL,OAAM;AAAA,QACN,aAAa;AAAA,QACb,YAAY;AAAA,QAEZ,8BAAC,6BACA,8BAAC,4BACA;AAAA,UAAC;AAAA;AAAA,YACA,MAAK;AAAA,YACL,WAAU;AAAA,YACV,SAAS,MAAM,qBAAqB,MAAM;AAAA,YAE1C;AAAA,kCAAC,UAAM,mBAAS,IAAI,SAAS,YAAY,IAAI,IAAI,SAAS,YAAY,GAAE;AAAA,cACxE,oBAAC,UAAK,WAAU,gCAAgC,yBAAc;AAAA;AAAA;AAAA,QAC/D,GACD,GACD;AAAA;AAAA,IACD;AAAA,KACD;AAEF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback } from "react";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
defineMessages,
|
|
5
|
+
EditorPortal,
|
|
6
|
+
useEditor,
|
|
7
|
+
useTranslation,
|
|
8
|
+
useValue
|
|
9
|
+
} from "tldraw";
|
|
4
10
|
import { CommentsList } from "../ui/comments-list.mjs";
|
|
5
11
|
import { UNKNOWN_COMMENT_AUTHOR } from "./comment-render.mjs";
|
|
6
12
|
import { CommentsFilterMenu } from "./comments-filter-menu.mjs";
|
|
@@ -11,6 +17,15 @@ import { useCommentingOptions } from "./options.mjs";
|
|
|
11
17
|
import { richTextToPlaintext } from "./rich-text.mjs";
|
|
12
18
|
import { commentsSidebarOpen, openThreadId, sidebarFilters } from "./state.mjs";
|
|
13
19
|
import { focusThread } from "./thread-state.mjs";
|
|
20
|
+
const messages = defineMessages({
|
|
21
|
+
commentsEmpty: { id: "comments.empty", defaultMessage: "No comments on this page yet." },
|
|
22
|
+
commentsEmptyFiltered: {
|
|
23
|
+
id: "comments.empty-filtered",
|
|
24
|
+
defaultMessage: "No comments match your filters."
|
|
25
|
+
},
|
|
26
|
+
commentsResolved: { id: "comments.resolved", defaultMessage: "Resolved" },
|
|
27
|
+
commentsTitle: { id: "comments.title", defaultMessage: "Comments" }
|
|
28
|
+
});
|
|
14
29
|
function CanvasCommentsSidebar(props) {
|
|
15
30
|
const { resolveAuthor, currentUserId, isCommentUnread, header, empty, getThreadHref } = props;
|
|
16
31
|
const resolveName = useCallback((id) => resolveAuthor(id)?.name, [resolveAuthor]);
|
|
@@ -83,19 +98,19 @@ function CanvasCommentsSidebar(props) {
|
|
|
83
98
|
CommentsList,
|
|
84
99
|
{
|
|
85
100
|
items,
|
|
86
|
-
header: header ?? msg(
|
|
101
|
+
header: header ?? msg(messages.commentsTitle),
|
|
87
102
|
headerAction: /* @__PURE__ */ jsxs("div", { className: "tlui-cmt-list__header-actions", children: [
|
|
103
|
+
/* @__PURE__ */ jsx(CommentsVisibilityToggle, {}),
|
|
88
104
|
/* @__PURE__ */ jsx(
|
|
89
105
|
CommentsFilterMenu,
|
|
90
106
|
{
|
|
91
107
|
canFilterByAuthor: currentUserId !== null,
|
|
92
108
|
canFilterByUnread: isCommentUnread !== void 0
|
|
93
109
|
}
|
|
94
|
-
)
|
|
95
|
-
/* @__PURE__ */ jsx(CommentsVisibilityToggle, {})
|
|
110
|
+
)
|
|
96
111
|
] }),
|
|
97
|
-
empty: items.length === 0 && pageThreads.length > 0 ? msg(
|
|
98
|
-
resolvedLabel: msg(
|
|
112
|
+
empty: items.length === 0 && pageThreads.length > 0 ? msg(messages.commentsEmptyFiltered) : empty ?? msg(messages.commentsEmpty),
|
|
113
|
+
resolvedLabel: msg(messages.commentsResolved),
|
|
99
114
|
onSelect: focus,
|
|
100
115
|
renderItem
|
|
101
116
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/canvas/comments-sidebar.tsx"],
|
|
4
|
-
"sourcesContent": ["import { ReactNode, useCallback } from 'react'\nimport {
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import { ReactNode, useCallback } from 'react'\nimport {\n\tdefineMessages,\n\tEditorPortal,\n\tTLCommentThread,\n\tuseEditor,\n\tuseTranslation,\n\tuseValue,\n} from 'tldraw'\nimport { CommentListItemProps, CommentListItemRenderProps, CommentsList } from '../ui/comments-list'\nimport { UNKNOWN_COMMENT_AUTHOR } from './comment-render'\nimport { CommentsFilterMenu } from './comments-filter-menu'\nimport { CommentsVisibilityToggle } from './comments-visibility-toggle'\nimport { type CommentingContext } from './context'\nimport { getCommentsByThread, useCommentThreads } from './hooks'\nimport { useCommentingEnabled } from './license'\nimport { useCommentingOptions } from './options'\nimport { richTextToPlaintext } from './rich-text'\nimport { commentsSidebarOpen, openThreadId, sidebarFilters } from './state'\nimport { focusThread } from './thread-state'\n\nconst messages = defineMessages({\n\tcommentsEmpty: { id: 'comments.empty', defaultMessage: 'No comments on this page yet.' },\n\tcommentsEmptyFiltered: {\n\t\tid: 'comments.empty-filtered',\n\t\tdefaultMessage: 'No comments match your filters.',\n\t},\n\tcommentsResolved: { id: 'comments.resolved', defaultMessage: 'Resolved' },\n\tcommentsTitle: { id: 'comments.title', defaultMessage: 'Comments' },\n})\n\n/**\n * The host wiring for {@link CanvasCommentsSidebar}: the {@link CommentingContext} fields it reads,\n * plus the panel's own slots. A non-null `currentUserId` enables the \"only your threads\" filter, and\n * an `isCommentUnread` the \"only unread\" one. `CanvasComments` takes the same fields, so a host\n * mounting both can spread one object into each.\n *\n * @public\n */\nexport interface CanvasCommentsSidebarProps extends Pick<\n\tCommentingContext,\n\t'currentUserId' | 'resolveAuthor' | 'isCommentUnread' | 'getThreadHref'\n> {\n\t/** Header above the list. */\n\theader?: ReactNode\n\t/** Shown when the page has no threads. */\n\tempty?: ReactNode\n}\n\n/**\n * A comments list panel for the current page, shown while {@link commentsSidebarOpen} is set (e.g.\n * toggled by a button). Clicking a thread brings its pin into view and opens it. Batteries-included\n * over the store (a sibling to `CanvasComments`); `CommentsList` is exported for a differently-placed\n * or always-on list.\n * @public @react\n */\nexport function CanvasCommentsSidebar(props: CanvasCommentsSidebarProps) {\n\tconst { resolveAuthor, currentUserId, isCommentUnread, header, empty, getThreadHref } = props\n\t// Name-only view of the resolver, for the plaintext previews (which resolve @-mentions).\n\tconst resolveName = useCallback((id: string) => resolveAuthor(id)?.name, [resolveAuthor])\n\tconst editor = useEditor()\n\tconst options = useCommentingOptions()\n\tconst commentingEnabled = useCommentingEnabled()\n\tconst msg = useTranslation()\n\tconst threads = useCommentThreads(editor)\n\tconst byThread = useValue('comments by thread', () => getCommentsByThread(editor).get(), [editor])\n\tconst currentPageId = useValue('page id', () => editor.getCurrentPageId(), [editor])\n\tconst open = useValue('sidebar open', () => commentsSidebarOpen.get(editor), [editor])\n\tconst openId = useValue('open thread', () => openThreadId.get(editor), [editor])\n\tconst filters = useValue('sidebar filters', () => sidebarFilters.get(editor), [editor])\n\tconst pageNames = useValue(\n\t\t'page names',\n\t\t() => new Map(editor.getPages().map((page) => [page.id, page.name])),\n\t\t[editor]\n\t)\n\n\tif (!commentingEnabled || !open) return null\n\n\t// Page scoping is treated as scoping, not a filter: an empty page reads \"no comments yet\",\n\t// while a list emptied by the toggles below reads \"nothing matches your filters\".\n\tconst pageThreads = threads.filter(\n\t\t(thread) => !filters.onlyCurrentPage || thread.pageId === currentPageId\n\t)\n\n\tconst rows: SidebarRow[] = pageThreads\n\t\t.filter((thread) => filters.showResolved || thread.resolved == null)\n\t\t// \"Only mine\" is ignored without a known user \u2014 otherwise a persisted onlyMine=true would\n\t\t// empty the list for a signed-out viewer, with the (hidden) toggle giving no way to clear it.\n\t\t.filter(\n\t\t\t(thread) => !filters.onlyMine || currentUserId === null || thread.createdBy === currentUserId\n\t\t)\n\t\t// \"Only unread\" is likewise ignored without a read-status source.\n\t\t.filter(\n\t\t\t(thread) =>\n\t\t\t\t!filters.onlyUnread ||\n\t\t\t\tisCommentUnread === undefined ||\n\t\t\t\t(byThread.get(thread.id) ?? []).some((c) => isCommentUnread(c.id))\n\t\t)\n\t\t.map((thread) => {\n\t\t\tconst threadComments = byThread.get(thread.id) ?? []\n\t\t\tconst first = threadComments[0]\n\t\t\t// Comments arrive oldest-first, so the last one is the thread's most recent activity.\n\t\t\tconst last = threadComments[threadComments.length - 1]\n\t\t\tlet preview: ReactNode = ''\n\t\t\t// The `ThreadPreview` component slot overrides the built-in plaintext default.\n\t\t\tconst ThreadPreview = options.components.ThreadPreview\n\t\t\tif (first) {\n\t\t\t\tpreview = ThreadPreview ? (\n\t\t\t\t\t<ThreadPreview comment={first} />\n\t\t\t\t) : (\n\t\t\t\t\trichTextToPlaintext(first.body, resolveName)\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn {\n\t\t\t\titem: {\n\t\t\t\t\tid: thread.id,\n\t\t\t\t\tauthor: resolveAuthor(thread.createdBy) ?? UNKNOWN_COMMENT_AUTHOR,\n\t\t\t\t\tpreview,\n\t\t\t\t\tdate: new Date((first ?? thread).createdAt).toISOString(),\n\t\t\t\t\tresolved: thread.resolved != null,\n\t\t\t\t\t// The page label only earns its place when it adds information: multiple pages\n\t\t\t\t\t// exist, and the thread is somewhere other than where you already are.\n\t\t\t\t\tpage:\n\t\t\t\t\t\tpageNames.size > 1 && thread.pageId !== currentPageId\n\t\t\t\t\t\t\t? pageNames.get(thread.pageId)\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\tcount: threadComments.length,\n\t\t\t\t\tselected: openId === thread.id,\n\t\t\t\t\thref: getThreadHref?.(thread.id),\n\t\t\t\t},\n\t\t\t\tlastActivity: (last ?? thread).createdAt,\n\t\t\t}\n\t\t})\n\n\tconst items = sortSidebarRows(rows).map((row) => row.item)\n\n\tconst focus = (id: string) => {\n\t\tconst thread = threads.find((t) => t.id === id)\n\t\tif (thread) focusThread(editor, thread)\n\t}\n\n\t// The `ThreadRow` component slot overrides the built-in row. It gets the thread record alongside\n\t// the summarized row, so host state on `thread.meta` is in reach without a second lookup. Indexed\n\t// once rather than scanned per row \u2014 this runs for every row on every render of the panel.\n\tconst ThreadRow = options.components.ThreadRow\n\tconst renderItem = ThreadRow\n\t\t? (() => {\n\t\t\t\t// Keyed by plain string: a row's id comes back as one, not as a branded thread id.\n\t\t\t\tconst threadsById = new Map<string, TLCommentThread>(\n\t\t\t\t\tthreads.map((thread) => [thread.id, thread])\n\t\t\t\t)\n\t\t\t\treturn (rowProps: CommentListItemRenderProps) => {\n\t\t\t\t\tconst thread = threadsById.get(rowProps.id)\n\t\t\t\t\t// Can't happen \u2014 the items are built from these threads \u2014 but a row with no thread has\n\t\t\t\t\t// nothing to render, and the slot's contract says the record is there.\n\t\t\t\t\tif (!thread) return null\n\t\t\t\t\treturn <ThreadRow {...rowProps} thread={thread} />\n\t\t\t\t}\n\t\t\t})()\n\t\t: undefined\n\n\treturn (\n\t\t<SidebarPanel>\n\t\t\t<CommentsList\n\t\t\t\titems={items}\n\t\t\t\theader={header ?? msg(messages.commentsTitle)}\n\t\t\t\theaderAction={\n\t\t\t\t\t<div className=\"tlui-cmt-list__header-actions\">\n\t\t\t\t\t\t<CommentsVisibilityToggle />\n\t\t\t\t\t\t<CommentsFilterMenu\n\t\t\t\t\t\t\tcanFilterByAuthor={currentUserId !== null}\n\t\t\t\t\t\t\tcanFilterByUnread={isCommentUnread !== undefined}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t}\n\t\t\t\tempty={\n\t\t\t\t\titems.length === 0 && pageThreads.length > 0\n\t\t\t\t\t\t? msg(messages.commentsEmptyFiltered)\n\t\t\t\t\t\t: (empty ?? msg(messages.commentsEmpty))\n\t\t\t\t}\n\t\t\t\tresolvedLabel={msg(messages.commentsResolved)}\n\t\t\t\tonSelect={focus}\n\t\t\t\trenderItem={renderItem}\n\t\t\t/>\n\t\t</SidebarPanel>\n\t)\n}\n\n/** A list row paired with the sort key that isn't part of what the row displays. @public */\nexport interface SidebarRow {\n\titem: CommentListItemProps\n\t/** When the thread's most recent comment was posted \u2014 what the list orders by. */\n\tlastActivity: number\n}\n\n/**\n * Order the list: unresolved threads first, then by most recent activity, id as a stable tiebreak.\n * Recency is the thread's *latest* comment, not its first, so a thread someone just replied to rises\n * to the top instead of staying wherever it was started. (The row still shows the thread's opening\n * comment and its date \u2014 that's what identifies the thread; only the ordering follows the replies.)\n *\n * Exported so a hand-built list can match the sidebar's ordering instead of re-deriving it.\n *\n * @public\n */\nexport function sortSidebarRows(rows: readonly SidebarRow[]): readonly SidebarRow[] {\n\treturn [...rows].sort(\n\t\t(a, b) =>\n\t\t\tNumber(!!a.item.resolved) - Number(!!b.item.resolved) ||\n\t\t\tb.lastActivity - a.lastActivity ||\n\t\t\t(a.item.id < b.item.id ? -1 : 1)\n\t)\n}\n\n/** The sidebar surface, portaled into the container. It scrolls its own list, so \u2014 unlike tldraw's\n * wheel-transparent panels \u2014 a wheel over it doesn't pan the canvas. */\nfunction SidebarPanel({ children }: { children: ReactNode }) {\n\treturn (\n\t\t<EditorPortal>\n\t\t\t<div className=\"tlui-cmt-canvas-sidebar\" onContextMenu={(e) => e.stopPropagation()}>\n\t\t\t\t{children}\n\t\t\t</div>\n\t\t</EditorPortal>\n\t)\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import { TldrawUiButton, useEditor, useTranslation, useValue } from "tldraw";
|
|
2
|
+
import { defineMessages, TldrawUiButton, useEditor, useTranslation, useValue } from "tldraw";
|
|
3
3
|
import { EyeClosedIcon, EyeOpenIcon } from "../ui/icons.mjs";
|
|
4
4
|
import { commentsHidden, toggleCommentsHidden } from "./state.mjs";
|
|
5
|
+
const messages = defineMessages({
|
|
6
|
+
commentsHide: { id: "comments.hide", defaultMessage: "Hide comments" },
|
|
7
|
+
commentsShow: { id: "comments.show", defaultMessage: "Show comments" }
|
|
8
|
+
});
|
|
5
9
|
function CommentsVisibilityToggle() {
|
|
6
10
|
const editor = useEditor();
|
|
7
11
|
const msg = useTranslation();
|
|
8
12
|
const hidden = useValue("comments hidden", () => commentsHidden.get(editor), [editor]);
|
|
9
|
-
const label = hidden ? msg(
|
|
13
|
+
const label = hidden ? msg(messages.commentsShow) : msg(messages.commentsHide);
|
|
10
14
|
return /* @__PURE__ */ jsx(
|
|
11
15
|
TldrawUiButton,
|
|
12
16
|
{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/canvas/comments-visibility-toggle.tsx"],
|
|
4
|
-
"sourcesContent": ["import { TldrawUiButton, useEditor, useTranslation, useValue } from 'tldraw'\nimport { EyeClosedIcon, EyeOpenIcon } from '../ui/icons'\nimport { commentsHidden, toggleCommentsHidden } from './state'\n\n/** The sidebar header's show/hide toggle for comment pins \u2014 an eye that closes while comments\n * are hidden. The same state as the Shift+C shortcut.\n * @public @react */\nexport function CommentsVisibilityToggle() {\n\tconst editor = useEditor()\n\tconst msg = useTranslation()\n\tconst hidden = useValue('comments hidden', () => commentsHidden.get(editor), [editor])\n\tconst label = hidden ? msg(
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["import { defineMessages, TldrawUiButton, useEditor, useTranslation, useValue } from 'tldraw'\nimport { EyeClosedIcon, EyeOpenIcon } from '../ui/icons'\nimport { commentsHidden, toggleCommentsHidden } from './state'\n\nconst messages = defineMessages({\n\tcommentsHide: { id: 'comments.hide', defaultMessage: 'Hide comments' },\n\tcommentsShow: { id: 'comments.show', defaultMessage: 'Show comments' },\n})\n\n/** The sidebar header's show/hide toggle for comment pins \u2014 an eye that closes while comments\n * are hidden. The same state as the Shift+C shortcut.\n * @public @react */\nexport function CommentsVisibilityToggle() {\n\tconst editor = useEditor()\n\tconst msg = useTranslation()\n\tconst hidden = useValue('comments hidden', () => commentsHidden.get(editor), [editor])\n\tconst label = hidden ? msg(messages.commentsShow) : msg(messages.commentsHide)\n\n\treturn (\n\t\t<TldrawUiButton\n\t\t\ttype=\"icon\"\n\t\t\ttooltip={label}\n\t\t\ttitle={label}\n\t\t\tclassName=\"tlui-cmt-header-btn\"\n\t\t\tonClick={() => toggleCommentsHidden(editor)}\n\t\t>\n\t\t\t{hidden ? <EyeClosedIcon /> : <EyeOpenIcon />}\n\t\t</TldrawUiButton>\n\t)\n}\n"],
|
|
5
|
+
"mappings": "AA0Ba;AA1Bb,SAAS,gBAAgB,gBAAgB,WAAW,gBAAgB,gBAAgB;AACpF,SAAS,eAAe,mBAAmB;AAC3C,SAAS,gBAAgB,4BAA4B;AAErD,MAAM,WAAW,eAAe;AAAA,EAC/B,cAAc,EAAE,IAAI,iBAAiB,gBAAgB,gBAAgB;AAAA,EACrE,cAAc,EAAE,IAAI,iBAAiB,gBAAgB,gBAAgB;AACtE,CAAC;AAKM,SAAS,2BAA2B;AAC1C,QAAM,SAAS,UAAU;AACzB,QAAM,MAAM,eAAe;AAC3B,QAAM,SAAS,SAAS,mBAAmB,MAAM,eAAe,IAAI,MAAM,GAAG,CAAC,MAAM,CAAC;AACrF,QAAM,QAAQ,SAAS,IAAI,SAAS,YAAY,IAAI,IAAI,SAAS,YAAY;AAE7E,SACC;AAAA,IAAC;AAAA;AAAA,MACA,MAAK;AAAA,MACL,SAAS;AAAA,MACT,OAAO;AAAA,MACP,WAAU;AAAA,MACV,SAAS,MAAM,qBAAqB,MAAM;AAAA,MAEzC,mBAAS,oBAAC,iBAAc,IAAK,oBAAC,eAAY;AAAA;AAAA,EAC5C;AAEF;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|