@tldraw/commenting 0.0.0-bootstrap
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/commenting.css +832 -0
- package/dist-cjs/canvas/cluster-input.js +40 -0
- package/dist-cjs/canvas/cluster-input.js.map +7 -0
- package/dist-cjs/canvas/comment-body.js +31 -0
- package/dist-cjs/canvas/comment-body.js.map +7 -0
- package/dist-cjs/canvas/comment-render.js +72 -0
- package/dist-cjs/canvas/comment-render.js.map +7 -0
- package/dist-cjs/canvas/comment-store.js +48 -0
- package/dist-cjs/canvas/comment-store.js.map +7 -0
- package/dist-cjs/canvas/comment-tool.js +160 -0
- package/dist-cjs/canvas/comment-tool.js.map +7 -0
- package/dist-cjs/canvas/comments-filter-menu.js +109 -0
- package/dist-cjs/canvas/comments-filter-menu.js.map +7 -0
- package/dist-cjs/canvas/comments-menu-item.js +42 -0
- package/dist-cjs/canvas/comments-menu-item.js.map +7 -0
- package/dist-cjs/canvas/comments-overflow-menu.js +64 -0
- package/dist-cjs/canvas/comments-overflow-menu.js.map +7 -0
- package/dist-cjs/canvas/comments-overlay.js +1081 -0
- package/dist-cjs/canvas/comments-overlay.js.map +7 -0
- package/dist-cjs/canvas/comments-sidebar.js +134 -0
- package/dist-cjs/canvas/comments-sidebar.js.map +7 -0
- package/dist-cjs/canvas/hooks.js +45 -0
- package/dist-cjs/canvas/hooks.js.map +7 -0
- package/dist-cjs/canvas/license.js +28 -0
- package/dist-cjs/canvas/license.js.map +7 -0
- package/dist-cjs/canvas/options.js +45 -0
- package/dist-cjs/canvas/options.js.map +7 -0
- package/dist-cjs/canvas/region-options.js +49 -0
- package/dist-cjs/canvas/region-options.js.map +7 -0
- package/dist-cjs/canvas/rich-text.js +28 -0
- package/dist-cjs/canvas/rich-text.js.map +7 -0
- package/dist-cjs/canvas/sidebar-filters.js +30 -0
- package/dist-cjs/canvas/sidebar-filters.js.map +7 -0
- package/dist-cjs/canvas/state.js +76 -0
- package/dist-cjs/canvas/state.js.map +7 -0
- package/dist-cjs/canvas/thread-state.js +81 -0
- package/dist-cjs/canvas/thread-state.js.map +7 -0
- package/dist-cjs/clustering/computeClusterTable.js +83 -0
- package/dist-cjs/clustering/computeClusterTable.js.map +7 -0
- package/dist-cjs/clustering/mst.js +117 -0
- package/dist-cjs/clustering/mst.js.map +7 -0
- package/dist-cjs/clustering/replay.js +275 -0
- package/dist-cjs/clustering/replay.js.map +7 -0
- package/dist-cjs/clustering/runtime.js +134 -0
- package/dist-cjs/clustering/runtime.js.map +7 -0
- package/dist-cjs/clustering/schedule.js +170 -0
- package/dist-cjs/clustering/schedule.js.map +7 -0
- package/dist-cjs/clustering/types.js +17 -0
- package/dist-cjs/clustering/types.js.map +7 -0
- package/dist-cjs/index.d.ts +868 -0
- package/dist-cjs/index.js +126 -0
- package/dist-cjs/index.js.map +7 -0
- package/dist-cjs/ui/avatar.js +43 -0
- package/dist-cjs/ui/avatar.js.map +7 -0
- package/dist-cjs/ui/byline.js +35 -0
- package/dist-cjs/ui/byline.js.map +7 -0
- package/dist-cjs/ui/comment-card.js +37 -0
- package/dist-cjs/ui/comment-card.js.map +7 -0
- package/dist-cjs/ui/comment-composer.js +136 -0
- package/dist-cjs/ui/comment-composer.js.map +7 -0
- package/dist-cjs/ui/comment-extensions.js +37 -0
- package/dist-cjs/ui/comment-extensions.js.map +7 -0
- package/dist-cjs/ui/comment-mention.js +42 -0
- package/dist-cjs/ui/comment-mention.js.map +7 -0
- package/dist-cjs/ui/comment-pin.js +29 -0
- package/dist-cjs/ui/comment-pin.js.map +7 -0
- package/dist-cjs/ui/comment-text.js +29 -0
- package/dist-cjs/ui/comment-text.js.map +7 -0
- package/dist-cjs/ui/comment-thread.js +45 -0
- package/dist-cjs/ui/comment-thread.js.map +7 -0
- package/dist-cjs/ui/comments-list.js +106 -0
- package/dist-cjs/ui/comments-list.js.map +7 -0
- package/dist-cjs/ui/count-badge.js +28 -0
- package/dist-cjs/ui/count-badge.js.map +7 -0
- package/dist-cjs/ui/empty-state.js +31 -0
- package/dist-cjs/ui/empty-state.js.map +7 -0
- package/dist-cjs/ui/format-time.js +46 -0
- package/dist-cjs/ui/format-time.js.map +7 -0
- package/dist-cjs/ui/mention-list.js +70 -0
- package/dist-cjs/ui/mention-list.js.map +7 -0
- package/dist-cjs/ui/mention-suggestion.js +206 -0
- package/dist-cjs/ui/mention-suggestion.js.map +7 -0
- package/dist-cjs/ui/mention.js +31 -0
- package/dist-cjs/ui/mention.js.map +7 -0
- package/dist-cjs/ui/reaction.js +31 -0
- package/dist-cjs/ui/reaction.js.map +7 -0
- package/dist-cjs/ui/reactions.js +34 -0
- package/dist-cjs/ui/reactions.js.map +7 -0
- package/dist-cjs/ui/render-markdown.js +63 -0
- package/dist-cjs/ui/render-markdown.js.map +7 -0
- package/dist-cjs/ui/send-button.js +28 -0
- package/dist-cjs/ui/send-button.js.map +7 -0
- package/dist-esm/canvas/cluster-input.mjs +20 -0
- package/dist-esm/canvas/cluster-input.mjs.map +7 -0
- package/dist-esm/canvas/comment-body.mjs +11 -0
- package/dist-esm/canvas/comment-body.mjs.map +7 -0
- package/dist-esm/canvas/comment-render.mjs +52 -0
- package/dist-esm/canvas/comment-render.mjs.map +7 -0
- package/dist-esm/canvas/comment-store.mjs +28 -0
- package/dist-esm/canvas/comment-store.mjs.map +7 -0
- package/dist-esm/canvas/comment-tool.mjs +142 -0
- package/dist-esm/canvas/comment-tool.mjs.map +7 -0
- package/dist-esm/canvas/comments-filter-menu.mjs +99 -0
- package/dist-esm/canvas/comments-filter-menu.mjs.map +7 -0
- package/dist-esm/canvas/comments-menu-item.mjs +22 -0
- package/dist-esm/canvas/comments-menu-item.mjs.map +7 -0
- package/dist-esm/canvas/comments-overflow-menu.mjs +53 -0
- package/dist-esm/canvas/comments-overflow-menu.mjs.map +7 -0
- package/dist-esm/canvas/comments-overlay.mjs +1094 -0
- package/dist-esm/canvas/comments-overlay.mjs.map +7 -0
- package/dist-esm/canvas/comments-sidebar.mjs +120 -0
- package/dist-esm/canvas/comments-sidebar.mjs.map +7 -0
- package/dist-esm/canvas/hooks.mjs +25 -0
- package/dist-esm/canvas/hooks.mjs.map +7 -0
- package/dist-esm/canvas/license.mjs +8 -0
- package/dist-esm/canvas/license.mjs.map +7 -0
- package/dist-esm/canvas/options.mjs +27 -0
- package/dist-esm/canvas/options.mjs.map +7 -0
- package/dist-esm/canvas/region-options.mjs +29 -0
- package/dist-esm/canvas/region-options.mjs.map +7 -0
- package/dist-esm/canvas/rich-text.mjs +8 -0
- package/dist-esm/canvas/rich-text.mjs.map +7 -0
- package/dist-esm/canvas/sidebar-filters.mjs +10 -0
- package/dist-esm/canvas/sidebar-filters.mjs.map +7 -0
- package/dist-esm/canvas/state.mjs +60 -0
- package/dist-esm/canvas/state.mjs.map +7 -0
- package/dist-esm/canvas/thread-state.mjs +61 -0
- package/dist-esm/canvas/thread-state.mjs.map +7 -0
- package/dist-esm/clustering/computeClusterTable.mjs +63 -0
- package/dist-esm/clustering/computeClusterTable.mjs.map +7 -0
- package/dist-esm/clustering/mst.mjs +97 -0
- package/dist-esm/clustering/mst.mjs.map +7 -0
- package/dist-esm/clustering/replay.mjs +255 -0
- package/dist-esm/clustering/replay.mjs.map +7 -0
- package/dist-esm/clustering/runtime.mjs +114 -0
- package/dist-esm/clustering/runtime.mjs.map +7 -0
- package/dist-esm/clustering/schedule.mjs +150 -0
- package/dist-esm/clustering/schedule.mjs.map +7 -0
- package/dist-esm/clustering/types.mjs +1 -0
- package/dist-esm/clustering/types.mjs.map +7 -0
- package/dist-esm/index.d.mts +868 -0
- package/dist-esm/index.mjs +139 -0
- package/dist-esm/index.mjs.map +7 -0
- package/dist-esm/ui/avatar.mjs +23 -0
- package/dist-esm/ui/avatar.mjs.map +7 -0
- package/dist-esm/ui/byline.mjs +15 -0
- package/dist-esm/ui/byline.mjs.map +7 -0
- package/dist-esm/ui/comment-card.mjs +17 -0
- package/dist-esm/ui/comment-card.mjs.map +7 -0
- package/dist-esm/ui/comment-composer.mjs +116 -0
- package/dist-esm/ui/comment-composer.mjs.map +7 -0
- package/dist-esm/ui/comment-extensions.mjs +17 -0
- package/dist-esm/ui/comment-extensions.mjs.map +7 -0
- package/dist-esm/ui/comment-mention.mjs +22 -0
- package/dist-esm/ui/comment-mention.mjs.map +7 -0
- package/dist-esm/ui/comment-pin.mjs +9 -0
- package/dist-esm/ui/comment-pin.mjs.map +7 -0
- package/dist-esm/ui/comment-text.mjs +9 -0
- package/dist-esm/ui/comment-text.mjs.map +7 -0
- package/dist-esm/ui/comment-thread.mjs +25 -0
- package/dist-esm/ui/comment-thread.mjs.map +7 -0
- package/dist-esm/ui/comments-list.mjs +86 -0
- package/dist-esm/ui/comments-list.mjs.map +7 -0
- package/dist-esm/ui/count-badge.mjs +8 -0
- package/dist-esm/ui/count-badge.mjs.map +7 -0
- package/dist-esm/ui/empty-state.mjs +11 -0
- package/dist-esm/ui/empty-state.mjs.map +7 -0
- package/dist-esm/ui/format-time.mjs +26 -0
- package/dist-esm/ui/format-time.mjs.map +7 -0
- package/dist-esm/ui/mention-list.mjs +50 -0
- package/dist-esm/ui/mention-list.mjs.map +7 -0
- package/dist-esm/ui/mention-suggestion.mjs +186 -0
- package/dist-esm/ui/mention-suggestion.mjs.map +7 -0
- package/dist-esm/ui/mention.mjs +11 -0
- package/dist-esm/ui/mention.mjs.map +7 -0
- package/dist-esm/ui/reaction.mjs +11 -0
- package/dist-esm/ui/reaction.mjs.map +7 -0
- package/dist-esm/ui/reactions.mjs +14 -0
- package/dist-esm/ui/reactions.mjs.map +7 -0
- package/dist-esm/ui/render-markdown.mjs +45 -0
- package/dist-esm/ui/render-markdown.mjs.map +7 -0
- package/dist-esm/ui/send-button.mjs +8 -0
- package/dist-esm/ui/send-button.mjs.map +7 -0
- package/package.json +71 -0
- package/src/canvas/canvas.css +148 -0
- package/src/canvas/cluster-input.test.ts +223 -0
- package/src/canvas/cluster-input.ts +29 -0
- package/src/canvas/comment-body.tsx +21 -0
- package/src/canvas/comment-render.test.ts +90 -0
- package/src/canvas/comment-render.ts +92 -0
- package/src/canvas/comment-store.ts +55 -0
- package/src/canvas/comment-tool.tsx +200 -0
- package/src/canvas/comments-filter-menu.tsx +104 -0
- package/src/canvas/comments-menu-item.tsx +24 -0
- package/src/canvas/comments-overflow-menu.tsx +62 -0
- package/src/canvas/comments-overlay.tsx +1460 -0
- package/src/canvas/comments-sidebar.tsx +184 -0
- package/src/canvas/hooks.ts +28 -0
- package/src/canvas/license.ts +15 -0
- package/src/canvas/options.test.ts +127 -0
- package/src/canvas/options.ts +111 -0
- package/src/canvas/region-options.ts +57 -0
- package/src/canvas/rich-text.ts +15 -0
- package/src/canvas/sidebar-filters.ts +25 -0
- package/src/canvas/state.ts +102 -0
- package/src/canvas/thread-state.ts +91 -0
- package/src/clustering/computeClusterTable.test.ts +299 -0
- package/src/clustering/computeClusterTable.ts +82 -0
- package/src/clustering/locality.test.ts +189 -0
- package/src/clustering/mst.test.ts +383 -0
- package/src/clustering/mst.ts +134 -0
- package/src/clustering/replay.test.ts +547 -0
- package/src/clustering/replay.ts +326 -0
- package/src/clustering/runtime.test.ts +596 -0
- package/src/clustering/runtime.ts +153 -0
- package/src/clustering/schedule.test.ts +514 -0
- package/src/clustering/schedule.ts +184 -0
- package/src/clustering/types.ts +102 -0
- package/src/index.ts +96 -0
- package/src/ui/avatar.tsx +31 -0
- package/src/ui/byline.tsx +23 -0
- package/src/ui/comment-card.tsx +32 -0
- package/src/ui/comment-composer.tsx +179 -0
- package/src/ui/comment-extensions.ts +25 -0
- package/src/ui/comment-mention.ts +47 -0
- package/src/ui/comment-pin.tsx +21 -0
- package/src/ui/comment-text.tsx +12 -0
- package/src/ui/comment-thread.tsx +56 -0
- package/src/ui/comments-list.tsx +139 -0
- package/src/ui/comments.css +678 -0
- package/src/ui/count-badge.tsx +9 -0
- package/src/ui/empty-state.tsx +16 -0
- package/src/ui/format-time.ts +29 -0
- package/src/ui/mention-list.tsx +106 -0
- package/src/ui/mention-suggestion.test.ts +18 -0
- package/src/ui/mention-suggestion.tsx +285 -0
- package/src/ui/mention.tsx +9 -0
- package/src/ui/reaction.tsx +16 -0
- package/src/ui/reactions.tsx +15 -0
- package/src/ui/render-markdown.tsx +72 -0
- package/src/ui/send-button.tsx +15 -0
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BoxModel,
|
|
3
|
+
StateNode,
|
|
4
|
+
TLCommentAnchor,
|
|
5
|
+
TLStateNodeConstructor,
|
|
6
|
+
TLUiOverrides,
|
|
7
|
+
VecLike,
|
|
8
|
+
} from 'tldraw'
|
|
9
|
+
import { type CommentingOptions, defaultCommentingOptions } from './options'
|
|
10
|
+
import { getRegionCommentOptions } from './region-options'
|
|
11
|
+
import { pendingComment, regionDraft } from './state'
|
|
12
|
+
import { regionPinPoint, shapeAnchorAt } from './thread-state'
|
|
13
|
+
|
|
14
|
+
/** A comment being placed but not yet posted: where its composer sits and what it will anchor
|
|
15
|
+
* to. Shared between the tool (which sets it on click) and the overlay (which renders the
|
|
16
|
+
* composer). Null when nothing is being placed. The atom itself lives in `./state`
|
|
17
|
+
* ({@link pendingComment}), scoped per editor.
|
|
18
|
+
* @public */
|
|
19
|
+
export interface PendingComment {
|
|
20
|
+
anchor: TLCommentAnchor
|
|
21
|
+
/** Page point where the composer opens (the click location, or a region's pin corner). */
|
|
22
|
+
point: VecLike
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Merge configure options over a base. Shallow for scalars; `components` is merged rather than
|
|
27
|
+
* replaced so chained `configure` calls layer their slots — a later `{ components: { PinContent } }`
|
|
28
|
+
* doesn't drop an earlier `{ components: { CommentBody } }`.
|
|
29
|
+
*/
|
|
30
|
+
function mergeCommentingOptions(
|
|
31
|
+
base: CommentingOptions,
|
|
32
|
+
overrides: Partial<CommentingOptions>
|
|
33
|
+
): CommentingOptions {
|
|
34
|
+
return {
|
|
35
|
+
...base,
|
|
36
|
+
...overrides,
|
|
37
|
+
components: { ...base.components, ...overrides.components },
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The page-space rectangle spanned by two points, normalized so w/h are non-negative. Shared by
|
|
42
|
+
* region creation (pointer-down → cursor) and corner resize (fixed corner → cursor). */
|
|
43
|
+
export function regionBetween(a: VecLike, b: VecLike): BoxModel {
|
|
44
|
+
return {
|
|
45
|
+
x: Math.min(a.x, b.x),
|
|
46
|
+
y: Math.min(a.y, b.y),
|
|
47
|
+
w: Math.abs(a.x - b.x),
|
|
48
|
+
h: Math.abs(a.y - b.y),
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The comment tool. A click starts a point/shape thread (the pin tracks the shape it lands on);
|
|
54
|
+
* dragging out a rectangle starts a region thread anchored to that area. Placement only opens a
|
|
55
|
+
* composer (via `pendingComment`); the records are created when the comment is posted.
|
|
56
|
+
* @public
|
|
57
|
+
*/
|
|
58
|
+
export class CommentTool extends StateNode {
|
|
59
|
+
static override id = 'comment'
|
|
60
|
+
static override initial = 'idle'
|
|
61
|
+
static override children(): TLStateNodeConstructor[] {
|
|
62
|
+
return [CommentIdle, CommentPointing, CommentDragging]
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Configure this tool's {@link CommentTool.options | `options`}, returning a configured subclass
|
|
67
|
+
* to register via `tools`. Mirrors `ShapeUtil.configure`. Layers over any prior `configure`, so
|
|
68
|
+
* calls can be chained.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```tsx
|
|
72
|
+
* <Tldraw tools={[CommentTool.configure({ history: 'ignore', enableClustering: false })]} />
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
static configure<T extends TLStateNodeConstructor>(
|
|
76
|
+
this: T,
|
|
77
|
+
options: T extends new (...args: any[]) => { options: infer Options } ? Partial<Options> : never
|
|
78
|
+
): T {
|
|
79
|
+
// @ts-expect-error -- mirrors ShapeUtil.configure; extending `this` is sound at runtime.
|
|
80
|
+
return class extends this {
|
|
81
|
+
// @ts-expect-error
|
|
82
|
+
options = mergeCommentingOptions(this.options, options)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The merged commenting options for this editor. Read from anywhere via
|
|
88
|
+
* {@link getCommentingOptions}. Override with {@link CommentTool.configure}.
|
|
89
|
+
*/
|
|
90
|
+
options: CommentingOptions = defaultCommentingOptions
|
|
91
|
+
|
|
92
|
+
override onEnter() {
|
|
93
|
+
this.editor.setCursor({ type: 'cross', rotation: 0 })
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Escape leaves the tool, like the built-in tools (the editor dispatches `cancel` on Escape).
|
|
97
|
+
override onCancel() {
|
|
98
|
+
this.editor.setCurrentTool('select')
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
class CommentIdle extends StateNode {
|
|
103
|
+
static override id = 'idle'
|
|
104
|
+
|
|
105
|
+
override onPointerDown() {
|
|
106
|
+
this.parent.transition('pointing')
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
class CommentPointing extends StateNode {
|
|
111
|
+
static override id = 'pointing'
|
|
112
|
+
|
|
113
|
+
// Once the pointer passes the editor's drag threshold this is a region, not a click — but only
|
|
114
|
+
// when region comments are enabled; otherwise a drag is treated as a click (point/shape).
|
|
115
|
+
override onPointerMove() {
|
|
116
|
+
if (getRegionCommentOptions(this.editor).enabled && this.editor.inputs.getIsDragging()) {
|
|
117
|
+
this.parent.transition('dragging')
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// A pointer up with no drag is a click: anchor to the shape under it, or drop a point.
|
|
122
|
+
override onPointerUp() {
|
|
123
|
+
const { editor } = this
|
|
124
|
+
const point = editor.inputs.getCurrentPagePoint()
|
|
125
|
+
const hit = editor.getShapeAtPoint(point, { hitInside: true })
|
|
126
|
+
const anchor: TLCommentAnchor = hit
|
|
127
|
+
? shapeAnchorAt(editor, hit.id, point, editor.inputs.getAltKey())
|
|
128
|
+
: { type: 'point', x: point.x, y: point.y }
|
|
129
|
+
pendingComment.set(editor, { anchor, point: { x: point.x, y: point.y } })
|
|
130
|
+
// Hand back to select; the open composer is now the focus.
|
|
131
|
+
editor.setCurrentTool('select')
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
class CommentDragging extends StateNode {
|
|
136
|
+
static override id = 'dragging'
|
|
137
|
+
|
|
138
|
+
override onEnter() {
|
|
139
|
+
this.updateDraft()
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
override onPointerMove() {
|
|
143
|
+
this.updateDraft()
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Commit the dragged rectangle as a region anchor; the composer opens at its pin corner.
|
|
147
|
+
override onPointerUp() {
|
|
148
|
+
const { editor } = this
|
|
149
|
+
const region = regionBetween(
|
|
150
|
+
editor.inputs.getOriginPagePoint(),
|
|
151
|
+
editor.inputs.getCurrentPagePoint()
|
|
152
|
+
)
|
|
153
|
+
regionDraft.set(editor, null)
|
|
154
|
+
pendingComment.set(editor, {
|
|
155
|
+
anchor: { type: 'region', ...region },
|
|
156
|
+
point: regionPinPoint(region, getRegionCommentOptions(editor).pinCorner),
|
|
157
|
+
})
|
|
158
|
+
editor.setCurrentTool('select')
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
override onCancel() {
|
|
162
|
+
this.cancel()
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
override onInterrupt() {
|
|
166
|
+
this.cancel()
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private updateDraft() {
|
|
170
|
+
const { editor } = this
|
|
171
|
+
regionDraft.set(
|
|
172
|
+
editor,
|
|
173
|
+
regionBetween(editor.inputs.getOriginPagePoint(), editor.inputs.getCurrentPagePoint())
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
private cancel() {
|
|
178
|
+
regionDraft.set(this.editor, null)
|
|
179
|
+
this.editor.setCurrentTool('select')
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** @public */
|
|
184
|
+
export const commentTools = [CommentTool]
|
|
185
|
+
|
|
186
|
+
/** Registers the comment tool in the UI (icon, label, shortcut). Compose into your overrides.
|
|
187
|
+
* Once registered, tldraw's `DefaultToolbarContent` shows the comment button next to the eraser.
|
|
188
|
+
* @public */
|
|
189
|
+
export const commentToolOverrides: TLUiOverrides = {
|
|
190
|
+
tools(editor, tools) {
|
|
191
|
+
tools.comment = {
|
|
192
|
+
id: 'comment',
|
|
193
|
+
icon: 'comment',
|
|
194
|
+
label: 'Comment',
|
|
195
|
+
kbd: 'c',
|
|
196
|
+
onSelect: () => editor.setCurrentTool('comment'),
|
|
197
|
+
}
|
|
198
|
+
return tools
|
|
199
|
+
},
|
|
200
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TldrawUiDropdownMenuContent,
|
|
3
|
+
TldrawUiDropdownMenuRoot,
|
|
4
|
+
TldrawUiDropdownMenuTrigger,
|
|
5
|
+
TldrawUiMenuCheckboxItem,
|
|
6
|
+
TldrawUiMenuContextProvider,
|
|
7
|
+
TldrawUiMenuGroup,
|
|
8
|
+
useEditor,
|
|
9
|
+
useTranslation,
|
|
10
|
+
useValue,
|
|
11
|
+
} from 'tldraw'
|
|
12
|
+
import { SidebarFilters } from './sidebar-filters'
|
|
13
|
+
import { sidebarFilters } from './state'
|
|
14
|
+
|
|
15
|
+
/** @public */
|
|
16
|
+
export interface CommentsFilterMenuProps {
|
|
17
|
+
/** Whether to offer the "only your threads" toggle (needs a known current user). */
|
|
18
|
+
canFilterByAuthor?: boolean
|
|
19
|
+
/** Whether to offer the "only unread" toggle (needs a read-status source). */
|
|
20
|
+
canFilterByUnread?: boolean
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** The funnel dropdown in the sidebar header: toggles for which threads the list shows.
|
|
24
|
+
* @public @react */
|
|
25
|
+
export function CommentsFilterMenu({
|
|
26
|
+
canFilterByAuthor,
|
|
27
|
+
canFilterByUnread,
|
|
28
|
+
}: CommentsFilterMenuProps) {
|
|
29
|
+
const editor = useEditor()
|
|
30
|
+
const msg = useTranslation()
|
|
31
|
+
const filters = useValue('sidebar filters', () => sidebarFilters.get(editor), [editor])
|
|
32
|
+
const toggle = (key: keyof SidebarFilters) => {
|
|
33
|
+
sidebarFilters.update(editor, (f) => ({ ...f, [key]: !f[key] }))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<TldrawUiDropdownMenuRoot id="comments-filter">
|
|
38
|
+
<TldrawUiDropdownMenuTrigger>
|
|
39
|
+
<button
|
|
40
|
+
type="button"
|
|
41
|
+
className="cmt-header-btn"
|
|
42
|
+
title={msg('comments.filter')}
|
|
43
|
+
aria-label={msg('comments.filter')}
|
|
44
|
+
>
|
|
45
|
+
<FilterIcon />
|
|
46
|
+
</button>
|
|
47
|
+
</TldrawUiDropdownMenuTrigger>
|
|
48
|
+
<TldrawUiDropdownMenuContent side="bottom" align="start">
|
|
49
|
+
<TldrawUiMenuContextProvider type="menu" sourceId="menu">
|
|
50
|
+
<TldrawUiMenuGroup id="comments-filter">
|
|
51
|
+
<TldrawUiMenuCheckboxItem
|
|
52
|
+
id="show-resolved"
|
|
53
|
+
label="comments.show-resolved"
|
|
54
|
+
checked={filters.showResolved}
|
|
55
|
+
onSelect={() => toggle('showResolved')}
|
|
56
|
+
/>
|
|
57
|
+
{canFilterByAuthor && (
|
|
58
|
+
<TldrawUiMenuCheckboxItem
|
|
59
|
+
id="only-mine"
|
|
60
|
+
label="comments.only-mine"
|
|
61
|
+
checked={filters.onlyMine}
|
|
62
|
+
onSelect={() => toggle('onlyMine')}
|
|
63
|
+
/>
|
|
64
|
+
)}
|
|
65
|
+
{canFilterByUnread && (
|
|
66
|
+
<TldrawUiMenuCheckboxItem
|
|
67
|
+
id="only-unread"
|
|
68
|
+
label="comments.only-unread"
|
|
69
|
+
checked={filters.onlyUnread}
|
|
70
|
+
onSelect={() => toggle('onlyUnread')}
|
|
71
|
+
/>
|
|
72
|
+
)}
|
|
73
|
+
<TldrawUiMenuCheckboxItem
|
|
74
|
+
id="only-current-page"
|
|
75
|
+
label="comments.only-current-page"
|
|
76
|
+
checked={filters.onlyCurrentPage}
|
|
77
|
+
onSelect={() => toggle('onlyCurrentPage')}
|
|
78
|
+
/>
|
|
79
|
+
</TldrawUiMenuGroup>
|
|
80
|
+
</TldrawUiMenuContextProvider>
|
|
81
|
+
</TldrawUiDropdownMenuContent>
|
|
82
|
+
</TldrawUiDropdownMenuRoot>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function FilterIcon() {
|
|
87
|
+
return (
|
|
88
|
+
<svg
|
|
89
|
+
width="16"
|
|
90
|
+
height="16"
|
|
91
|
+
viewBox="0 0 16 16"
|
|
92
|
+
fill="none"
|
|
93
|
+
stroke="currentColor"
|
|
94
|
+
strokeWidth="1.5"
|
|
95
|
+
strokeLinecap="round"
|
|
96
|
+
aria-hidden="true"
|
|
97
|
+
>
|
|
98
|
+
<line x1="2.5" y1="5" x2="13.5" y2="5" />
|
|
99
|
+
<line x1="2.5" y1="11" x2="13.5" y2="11" />
|
|
100
|
+
<circle cx="6" cy="5" r="1.9" fill="currentColor" stroke="none" />
|
|
101
|
+
<circle cx="10.5" cy="11" r="1.9" fill="currentColor" stroke="none" />
|
|
102
|
+
</svg>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { TldrawUiMenuCheckboxItem, useEditor, useValue } from 'tldraw'
|
|
2
|
+
import { commentsHidden, toggleCommentsHidden } from './state'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A checkbox menu item for a "View" menu that shows/hides comment pins on the canvas — checked
|
|
6
|
+
* when comments are visible, like tldraw's own grid/snap toggles. Reads and toggles the shared
|
|
7
|
+
* {@link commentsHidden} signal, so it stays in sync with the Shift+C shortcut and the sidebar
|
|
8
|
+
* control. Drop it into whichever menu your app owns.
|
|
9
|
+
* @public @react
|
|
10
|
+
*/
|
|
11
|
+
export function CommentsMenuItem() {
|
|
12
|
+
const editor = useEditor()
|
|
13
|
+
const hidden = useValue('comments hidden', () => commentsHidden.get(editor), [editor])
|
|
14
|
+
return (
|
|
15
|
+
<TldrawUiMenuCheckboxItem
|
|
16
|
+
id="comments-visible"
|
|
17
|
+
label="comments.title"
|
|
18
|
+
kbd="!c"
|
|
19
|
+
readonlyOk
|
|
20
|
+
checked={!hidden}
|
|
21
|
+
onSelect={() => toggleCommentsHidden(editor)}
|
|
22
|
+
/>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TldrawUiDropdownMenuContent,
|
|
3
|
+
TldrawUiDropdownMenuGroup,
|
|
4
|
+
TldrawUiDropdownMenuItem,
|
|
5
|
+
TldrawUiDropdownMenuRoot,
|
|
6
|
+
TldrawUiDropdownMenuTrigger,
|
|
7
|
+
useEditor,
|
|
8
|
+
useTranslation,
|
|
9
|
+
useValue,
|
|
10
|
+
} from 'tldraw'
|
|
11
|
+
import { commentsHidden, toggleCommentsHidden } from './state'
|
|
12
|
+
|
|
13
|
+
// A keyboard-shortcut glyph, not translatable copy — kept out of JSX as a constant.
|
|
14
|
+
const HIDE_SHORTCUT = '⇧C'
|
|
15
|
+
|
|
16
|
+
/** The overflow (⋯) dropdown in the sidebar header. For now it holds the hide/show-comments
|
|
17
|
+
* toggle; it's the home for later comment-wide controls (notifications, mark all as read).
|
|
18
|
+
* @public @react */
|
|
19
|
+
export function CommentsOverflowMenu() {
|
|
20
|
+
const editor = useEditor()
|
|
21
|
+
const msg = useTranslation()
|
|
22
|
+
const hidden = useValue('comments hidden', () => commentsHidden.get(editor), [editor])
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<TldrawUiDropdownMenuRoot id="comments-overflow">
|
|
26
|
+
<TldrawUiDropdownMenuTrigger>
|
|
27
|
+
<button
|
|
28
|
+
type="button"
|
|
29
|
+
className="cmt-header-btn"
|
|
30
|
+
title={msg('comments.more-options')}
|
|
31
|
+
aria-label={msg('comments.more-options')}
|
|
32
|
+
>
|
|
33
|
+
<MoreIcon />
|
|
34
|
+
</button>
|
|
35
|
+
</TldrawUiDropdownMenuTrigger>
|
|
36
|
+
<TldrawUiDropdownMenuContent side="bottom" align="end">
|
|
37
|
+
<TldrawUiDropdownMenuGroup>
|
|
38
|
+
<TldrawUiDropdownMenuItem>
|
|
39
|
+
<button
|
|
40
|
+
type="button"
|
|
41
|
+
className="cmt-menu-item"
|
|
42
|
+
onClick={() => toggleCommentsHidden(editor)}
|
|
43
|
+
>
|
|
44
|
+
<span>{hidden ? msg('comments.show') : msg('comments.hide')}</span>
|
|
45
|
+
<span className="cmt-menu-item__shortcut">{HIDE_SHORTCUT}</span>
|
|
46
|
+
</button>
|
|
47
|
+
</TldrawUiDropdownMenuItem>
|
|
48
|
+
</TldrawUiDropdownMenuGroup>
|
|
49
|
+
</TldrawUiDropdownMenuContent>
|
|
50
|
+
</TldrawUiDropdownMenuRoot>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function MoreIcon() {
|
|
55
|
+
return (
|
|
56
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
|
|
57
|
+
<circle cx="3" cy="8" r="1.4" />
|
|
58
|
+
<circle cx="8" cy="8" r="1.4" />
|
|
59
|
+
<circle cx="13" cy="8" r="1.4" />
|
|
60
|
+
</svg>
|
|
61
|
+
)
|
|
62
|
+
}
|