@tldraw/commenting 0.0.0-bootstrap → 5.3.0-internal.1640468db8fd
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/commenting.css +284 -153
- package/dist-cjs/canvas/comment-body.js +1 -1
- package/dist-cjs/canvas/comment-body.js.map +2 -2
- package/dist-cjs/canvas/comment-drafts.js +54 -0
- package/dist-cjs/canvas/comment-drafts.js.map +7 -0
- package/dist-cjs/canvas/comment-render.js +2 -0
- package/dist-cjs/canvas/comment-render.js.map +2 -2
- package/dist-cjs/canvas/comment-tool.js +65 -12
- package/dist-cjs/canvas/comment-tool.js.map +2 -2
- package/dist-cjs/canvas/comments-filter-menu.js +54 -63
- package/dist-cjs/canvas/comments-filter-menu.js.map +2 -2
- package/dist-cjs/canvas/comments-overflow-menu.js +21 -27
- package/dist-cjs/canvas/comments-overflow-menu.js.map +2 -2
- package/dist-cjs/canvas/comments-overlay.js +304 -112
- package/dist-cjs/canvas/comments-overlay.js.map +2 -2
- package/dist-cjs/canvas/comments-sidebar.js +12 -18
- package/dist-cjs/canvas/comments-sidebar.js.map +2 -2
- package/dist-cjs/canvas/comments-visibility-toggle.js +66 -0
- package/dist-cjs/canvas/comments-visibility-toggle.js.map +7 -0
- package/dist-cjs/canvas/options.js +1 -0
- package/dist-cjs/canvas/options.js.map +2 -2
- package/dist-cjs/canvas/sidebar-filters.js +2 -2
- package/dist-cjs/canvas/sidebar-filters.js.map +2 -2
- package/dist-cjs/canvas/state.js +11 -0
- package/dist-cjs/canvas/state.js.map +2 -2
- package/dist-cjs/canvas/thread-state.js +8 -1
- package/dist-cjs/canvas/thread-state.js.map +2 -2
- package/dist-cjs/clustering/runtime.js +119 -7
- package/dist-cjs/clustering/runtime.js.map +2 -2
- package/dist-cjs/index.d.ts +126 -41
- package/dist-cjs/index.js +7 -1
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/ui/avatar.js +14 -6
- package/dist-cjs/ui/avatar.js.map +2 -2
- package/dist-cjs/ui/byline.js +4 -4
- package/dist-cjs/ui/byline.js.map +2 -2
- package/dist-cjs/ui/comment-author.js +17 -0
- package/dist-cjs/ui/comment-author.js.map +7 -0
- package/dist-cjs/ui/comment-card.js +4 -4
- package/dist-cjs/ui/comment-card.js.map +2 -2
- package/dist-cjs/ui/comment-composer.js +92 -29
- package/dist-cjs/ui/comment-composer.js.map +2 -2
- package/dist-cjs/ui/comment-mention.js +1 -1
- package/dist-cjs/ui/comment-mention.js.map +2 -2
- package/dist-cjs/ui/comment-pin.js +23 -3
- package/dist-cjs/ui/comment-pin.js.map +2 -2
- package/dist-cjs/ui/comment-text.js +1 -1
- package/dist-cjs/ui/comment-text.js.map +2 -2
- package/dist-cjs/ui/comment-thread.js +6 -6
- package/dist-cjs/ui/comment-thread.js.map +2 -2
- package/dist-cjs/ui/comments-list.js +12 -12
- package/dist-cjs/ui/comments-list.js.map +2 -2
- package/dist-cjs/ui/count-badge.js +1 -1
- package/dist-cjs/ui/count-badge.js.map +2 -2
- package/dist-cjs/ui/empty-state.js +3 -3
- package/dist-cjs/ui/empty-state.js.map +2 -2
- package/dist-cjs/ui/format-time.js +3 -0
- package/dist-cjs/ui/format-time.js.map +2 -2
- package/dist-cjs/ui/mention-list.js +8 -8
- package/dist-cjs/ui/mention-list.js.map +2 -2
- package/dist-cjs/ui/mention-suggestion.js +3 -3
- package/dist-cjs/ui/mention-suggestion.js.map +2 -2
- package/dist-cjs/ui/mention.js +1 -1
- package/dist-cjs/ui/mention.js.map +2 -2
- package/dist-cjs/ui/reaction.js +11 -4
- package/dist-cjs/ui/reaction.js.map +2 -2
- package/dist-cjs/ui/reactions.js +10 -2
- package/dist-cjs/ui/reactions.js.map +2 -2
- package/dist-cjs/ui/send-button.js +1 -1
- package/dist-cjs/ui/send-button.js.map +2 -2
- package/dist-cjs/ui/tooltip-button.js +32 -0
- package/dist-cjs/ui/tooltip-button.js.map +7 -0
- package/dist-esm/canvas/comment-body.mjs +1 -1
- package/dist-esm/canvas/comment-body.mjs.map +2 -2
- package/dist-esm/canvas/comment-drafts.mjs +34 -0
- package/dist-esm/canvas/comment-drafts.mjs.map +7 -0
- package/dist-esm/canvas/comment-render.mjs +2 -0
- package/dist-esm/canvas/comment-render.mjs.map +2 -2
- package/dist-esm/canvas/comment-tool.mjs +67 -14
- package/dist-esm/canvas/comment-tool.mjs.map +2 -2
- package/dist-esm/canvas/comments-filter-menu.mjs +54 -63
- package/dist-esm/canvas/comments-filter-menu.mjs.map +2 -2
- package/dist-esm/canvas/comments-overflow-menu.mjs +22 -27
- package/dist-esm/canvas/comments-overflow-menu.mjs.map +2 -2
- package/dist-esm/canvas/comments-overlay.mjs +322 -114
- package/dist-esm/canvas/comments-overlay.mjs.map +2 -2
- package/dist-esm/canvas/comments-sidebar.mjs +15 -21
- package/dist-esm/canvas/comments-sidebar.mjs.map +2 -2
- package/dist-esm/canvas/comments-visibility-toggle.mjs +46 -0
- package/dist-esm/canvas/comments-visibility-toggle.mjs.map +7 -0
- package/dist-esm/canvas/options.mjs +1 -0
- package/dist-esm/canvas/options.mjs.map +2 -2
- package/dist-esm/canvas/sidebar-filters.mjs +2 -2
- package/dist-esm/canvas/sidebar-filters.mjs.map +2 -2
- package/dist-esm/canvas/state.mjs +11 -0
- package/dist-esm/canvas/state.mjs.map +2 -2
- package/dist-esm/canvas/thread-state.mjs +8 -1
- package/dist-esm/canvas/thread-state.mjs.map +2 -2
- package/dist-esm/clustering/runtime.mjs +119 -7
- package/dist-esm/clustering/runtime.mjs.map +2 -2
- package/dist-esm/index.d.mts +126 -41
- package/dist-esm/index.mjs +11 -1
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/ui/avatar.mjs +14 -6
- package/dist-esm/ui/avatar.mjs.map +2 -2
- package/dist-esm/ui/byline.mjs +4 -4
- package/dist-esm/ui/byline.mjs.map +2 -2
- package/dist-esm/ui/comment-author.mjs +1 -0
- package/dist-esm/ui/comment-author.mjs.map +7 -0
- package/dist-esm/ui/comment-card.mjs +4 -4
- package/dist-esm/ui/comment-card.mjs.map +2 -2
- package/dist-esm/ui/comment-composer.mjs +93 -30
- package/dist-esm/ui/comment-composer.mjs.map +2 -2
- package/dist-esm/ui/comment-mention.mjs +1 -1
- package/dist-esm/ui/comment-mention.mjs.map +2 -2
- package/dist-esm/ui/comment-pin.mjs +23 -3
- package/dist-esm/ui/comment-pin.mjs.map +2 -2
- package/dist-esm/ui/comment-text.mjs +1 -1
- package/dist-esm/ui/comment-text.mjs.map +2 -2
- package/dist-esm/ui/comment-thread.mjs +6 -6
- package/dist-esm/ui/comment-thread.mjs.map +2 -2
- package/dist-esm/ui/comments-list.mjs +12 -12
- package/dist-esm/ui/comments-list.mjs.map +2 -2
- package/dist-esm/ui/count-badge.mjs +1 -1
- package/dist-esm/ui/count-badge.mjs.map +2 -2
- package/dist-esm/ui/empty-state.mjs +3 -3
- package/dist-esm/ui/empty-state.mjs.map +2 -2
- package/dist-esm/ui/format-time.mjs +3 -0
- package/dist-esm/ui/format-time.mjs.map +2 -2
- package/dist-esm/ui/mention-list.mjs +8 -8
- package/dist-esm/ui/mention-list.mjs.map +2 -2
- package/dist-esm/ui/mention-suggestion.mjs +3 -3
- package/dist-esm/ui/mention-suggestion.mjs.map +2 -2
- package/dist-esm/ui/mention.mjs +1 -1
- package/dist-esm/ui/mention.mjs.map +2 -2
- package/dist-esm/ui/reaction.mjs +11 -4
- package/dist-esm/ui/reaction.mjs.map +2 -2
- package/dist-esm/ui/reactions.mjs +10 -2
- package/dist-esm/ui/reactions.mjs.map +2 -2
- package/dist-esm/ui/send-button.mjs +1 -1
- package/dist-esm/ui/send-button.mjs.map +2 -2
- package/dist-esm/ui/tooltip-button.mjs +12 -0
- package/dist-esm/ui/tooltip-button.mjs.map +7 -0
- package/package.json +3 -3
- package/src/canvas/canvas.css +79 -36
- package/src/canvas/comment-body.tsx +1 -1
- package/src/canvas/comment-drafts.test.ts +55 -0
- package/src/canvas/comment-drafts.ts +46 -0
- package/src/canvas/comment-render.test.ts +1 -1
- package/src/canvas/comment-render.ts +7 -2
- package/src/canvas/comment-tool.tsx +98 -17
- package/src/canvas/comments-filter-menu.tsx +27 -33
- package/src/canvas/comments-overflow-menu.tsx +13 -21
- package/src/canvas/comments-overlay.tsx +441 -145
- package/src/canvas/comments-sidebar.tsx +27 -28
- package/src/canvas/comments-visibility-toggle.tsx +50 -0
- package/src/canvas/options.ts +25 -0
- package/src/canvas/sidebar-filters.ts +4 -3
- package/src/canvas/state.ts +22 -0
- package/src/canvas/thread-state.ts +16 -3
- package/src/clustering/runtime.test.ts +183 -7
- package/src/clustering/runtime.ts +164 -15
- package/src/index.ts +7 -0
- package/src/ui/avatar.tsx +15 -11
- package/src/ui/byline.tsx +6 -5
- package/src/ui/comment-author.ts +15 -0
- package/src/ui/comment-card.tsx +6 -5
- package/src/ui/comment-composer.tsx +122 -33
- package/src/ui/comment-mention.ts +2 -2
- package/src/ui/comment-pin.tsx +35 -4
- package/src/ui/comment-text.tsx +1 -1
- package/src/ui/comment-thread.tsx +8 -6
- package/src/ui/comments-list.tsx +19 -14
- package/src/ui/comments.css +205 -117
- package/src/ui/count-badge.tsx +1 -1
- package/src/ui/empty-state.tsx +3 -3
- package/src/ui/format-time.ts +5 -0
- package/src/ui/mention-list.tsx +13 -16
- package/src/ui/mention-suggestion.tsx +3 -3
- package/src/ui/mention.tsx +1 -1
- package/src/ui/reaction.tsx +6 -3
- package/src/ui/reactions.tsx +6 -2
- package/src/ui/send-button.tsx +1 -1
- package/src/ui/tooltip-button.tsx +20 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { deleteFromLocalStorage, getFromLocalStorage, setInLocalStorage } from '@tldraw/utils'
|
|
2
|
+
import type { TLRichText } from 'tldraw'
|
|
3
|
+
import { isCommentEmpty } from '../ui/comment-extensions'
|
|
4
|
+
|
|
5
|
+
const DRAFT_KEY_PREFIX = 'tldraw-comment-draft'
|
|
6
|
+
|
|
7
|
+
/** The single slot for an unsent new-comment (placement composer) draft. The anchor is re-picked
|
|
8
|
+
* by clicking when the composer reopens, so only the text is worth keeping — and without a
|
|
9
|
+
* universal document id in the SDK, one slot is the honest scope. */
|
|
10
|
+
export const NEW_COMMENT_DRAFT = 'new'
|
|
11
|
+
|
|
12
|
+
/** The draft slot for an unsent reply on a thread. Thread record ids are globally unique and
|
|
13
|
+
* stable across reloads, so replies never need a document key. @public */
|
|
14
|
+
export function replyDraftSlot(threadId: string): string {
|
|
15
|
+
return `reply:${threadId}`
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Read a saved unsent draft, or undefined when there is none (or it fails to parse). @public */
|
|
19
|
+
export function getCommentDraft(slot: string): TLRichText | undefined {
|
|
20
|
+
const raw = getFromLocalStorage(`${DRAFT_KEY_PREFIX}:${slot}`)
|
|
21
|
+
if (!raw) return undefined
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(raw) as TLRichText
|
|
24
|
+
} catch {
|
|
25
|
+
return undefined
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Save an unsent draft so click-away and reload don't lose it — the flip side of not warning
|
|
31
|
+
* before discard. Saving empty content clears the slot instead, so deleting your text and
|
|
32
|
+
* closing doesn't resurrect an empty draft later.
|
|
33
|
+
* @public
|
|
34
|
+
*/
|
|
35
|
+
export function saveCommentDraft(slot: string, value: TLRichText) {
|
|
36
|
+
if (isCommentEmpty(value)) {
|
|
37
|
+
clearCommentDraft(slot)
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
setInLocalStorage(`${DRAFT_KEY_PREFIX}:${slot}`, JSON.stringify(value))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Remove a saved draft (after posting, or when its content was emptied). @public */
|
|
44
|
+
export function clearCommentDraft(slot: string) {
|
|
45
|
+
deleteFromLocalStorage(`${DRAFT_KEY_PREFIX}:${slot}`)
|
|
46
|
+
}
|
|
@@ -38,7 +38,7 @@ describe('renderCommentHtml', () => {
|
|
|
38
38
|
it('renders a mention as a pill and resolves its id to the current name', () => {
|
|
39
39
|
const body = doc(para(text('hey '), mention('u1', 'Ada')))
|
|
40
40
|
const html = renderCommentHtml(body, (id) => (id === 'u1' ? 'Ada Lovelace' : '?'))
|
|
41
|
-
expect(html).toContain('cmt-mention')
|
|
41
|
+
expect(html).toContain('tlui-cmt-mention')
|
|
42
42
|
// the live name from the resolver, not the label stored at insert time
|
|
43
43
|
expect(html).toContain('@Ada Lovelace')
|
|
44
44
|
expect(html).not.toContain('@Ada<')
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
import { generateHTML, generateText, JSONContent } from '@tiptap/core'
|
|
2
2
|
import { TLRichText } from 'tldraw'
|
|
3
|
+
import { CommentAuthor } from '../ui/comment-author'
|
|
3
4
|
import { commentTipTapExtensions, isCommentEmpty } from '../ui/comment-extensions'
|
|
4
5
|
import { commentMention } from '../ui/comment-mention'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* The author name shown in a byline when no source can name an id (e.g. a deleted account, or a
|
|
8
9
|
* member with no comment and no live presence). The toolkit's one generic default, applied where a
|
|
9
|
-
* byline needs a name; hosts pre-empt it by
|
|
10
|
-
* key — a single English literal
|
|
10
|
+
* byline needs a name; hosts pre-empt it by resolving the id from `resolveAuthor`. Not a
|
|
11
|
+
* translation key — a single English literal.
|
|
11
12
|
*/
|
|
12
13
|
export const UNKNOWN_AUTHOR = 'Someone'
|
|
13
14
|
|
|
15
|
+
/** The `CommentAuthor` fallback for an unresolvable id — just the name, no color or image.
|
|
16
|
+
* Frozen: the one shared instance is handed to host render slots. */
|
|
17
|
+
export const UNKNOWN_COMMENT_AUTHOR: CommentAuthor = Object.freeze({ name: UNKNOWN_AUTHOR })
|
|
18
|
+
|
|
14
19
|
/**
|
|
15
20
|
* The comment extension set has no heading node, so a body that nonetheless contains one (e.g. a
|
|
16
21
|
* record created programmatically or synced from a client with a fuller set) would make TipTap
|
|
@@ -6,9 +6,9 @@ import {
|
|
|
6
6
|
TLUiOverrides,
|
|
7
7
|
VecLike,
|
|
8
8
|
} from 'tldraw'
|
|
9
|
-
import { type CommentingOptions, defaultCommentingOptions } from './options'
|
|
9
|
+
import { type CommentingOptions, defaultCommentingOptions, getCommentingOptions } from './options'
|
|
10
10
|
import { getRegionCommentOptions } from './region-options'
|
|
11
|
-
import { pendingComment, regionDraft } from './state'
|
|
11
|
+
import { commentsSidebarOpen, pendingComment, regionDraft } from './state'
|
|
12
12
|
import { regionPinPoint, shapeAnchorAt } from './thread-state'
|
|
13
13
|
|
|
14
14
|
/** A comment being placed but not yet posted: where its composer sits and what it will anchor
|
|
@@ -50,9 +50,11 @@ export function regionBetween(a: VecLike, b: VecLike): BoxModel {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
|
-
* The comment tool.
|
|
54
|
-
*
|
|
55
|
-
*
|
|
53
|
+
* The comment tool. Pressing down opens the comment composer immediately at the pointer, and it
|
|
54
|
+
* follows the pointer until release — like placing a sticky note — settling on a point, or on a
|
|
55
|
+
* shape when released over one. (With region comments enabled, dragging past the threshold instead
|
|
56
|
+
* draws a region rectangle.) Placement only opens a composer (via `pendingComment`); the records
|
|
57
|
+
* are created when the comment is posted.
|
|
56
58
|
* @public
|
|
57
59
|
*/
|
|
58
60
|
export class CommentTool extends StateNode {
|
|
@@ -90,7 +92,16 @@ export class CommentTool extends StateNode {
|
|
|
90
92
|
options: CommentingOptions = defaultCommentingOptions
|
|
91
93
|
|
|
92
94
|
override onEnter() {
|
|
93
|
-
this.editor.setCursor({ type: '
|
|
95
|
+
this.editor.setCursor({ type: 'comment', rotation: 0 })
|
|
96
|
+
// Placing comments is canvas-focused — the thread list gets out of the way while the tool is
|
|
97
|
+
// active. Reopened via its own control (a button), never by leaving the tool.
|
|
98
|
+
commentsSidebarOpen.set(this.editor, false)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
override onExit() {
|
|
102
|
+
// Drop the hover hint painted while pointing at shapes (see CommentIdle). The cursor resets
|
|
103
|
+
// when the next tool takes over.
|
|
104
|
+
this.editor.setHintingShapes([])
|
|
94
105
|
}
|
|
95
106
|
|
|
96
107
|
// Escape leaves the tool, like the built-in tools (the editor dispatches `cancel` on Escape).
|
|
@@ -102,40 +113,109 @@ export class CommentTool extends StateNode {
|
|
|
102
113
|
class CommentIdle extends StateNode {
|
|
103
114
|
static override id = 'idle'
|
|
104
115
|
|
|
116
|
+
override onEnter() {
|
|
117
|
+
// Back to hovering: restore the pin cursor (a prior placing state may have hidden it).
|
|
118
|
+
this.editor.setCursor({ type: 'comment', rotation: 0 })
|
|
119
|
+
this.updateHint()
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Hint the shape a click would attach to, using the same hit-test as onPointerUp below. Hinting
|
|
123
|
+
// shapes render an indicator ungated by the active tool, so this shows the select-style outline
|
|
124
|
+
// while the comment tool — not select — is active.
|
|
125
|
+
override onPointerMove() {
|
|
126
|
+
this.updateHint()
|
|
127
|
+
}
|
|
128
|
+
|
|
105
129
|
override onPointerDown() {
|
|
106
130
|
this.parent.transition('pointing')
|
|
107
131
|
}
|
|
132
|
+
|
|
133
|
+
private updateHint() {
|
|
134
|
+
const { editor } = this
|
|
135
|
+
const hit = editor.getShapeAtPoint(editor.inputs.getCurrentPagePoint(), { hitInside: true })
|
|
136
|
+
editor.setHintingShapes(hit ? [hit.id] : [])
|
|
137
|
+
}
|
|
108
138
|
}
|
|
109
139
|
|
|
110
140
|
class CommentPointing extends StateNode {
|
|
111
141
|
static override id = 'pointing'
|
|
112
142
|
|
|
113
|
-
|
|
114
|
-
|
|
143
|
+
override onEnter() {
|
|
144
|
+
// Open the composer immediately at the press point so it's visible from pointer-down (not just
|
|
145
|
+
// on release), and let it trail the pointer while dragging — like placing a sticky note. The
|
|
146
|
+
// anchor is a bare point for now; it's resolved (shape or point) on pointer up. Drop the idle
|
|
147
|
+
// hover hint so a region drag doesn't leave a stale single-shape outline under the dashed box.
|
|
148
|
+
const { editor } = this
|
|
149
|
+
editor.setHintingShapes([])
|
|
150
|
+
// Hide the cursor while placing: the draft composer is the pointer's stand-in now, so the pin
|
|
151
|
+
// cursor sitting over it just reads as clutter.
|
|
152
|
+
editor.setCursor({ type: 'none', rotation: 0 })
|
|
153
|
+
const point = editor.inputs.getCurrentPagePoint()
|
|
154
|
+
pendingComment.set(editor, {
|
|
155
|
+
anchor: { type: 'point', x: point.x, y: point.y },
|
|
156
|
+
point: { x: point.x, y: point.y },
|
|
157
|
+
})
|
|
158
|
+
}
|
|
159
|
+
|
|
115
160
|
override onPointerMove() {
|
|
116
|
-
|
|
161
|
+
const { editor } = this
|
|
162
|
+
// Once the pointer passes the drag threshold with region comments enabled, this is a region,
|
|
163
|
+
// not a follow — hand off to the region drag (which clears this composer and draws the box).
|
|
164
|
+
if (getRegionCommentOptions(editor).enabled && editor.inputs.getIsDragging()) {
|
|
117
165
|
this.parent.transition('dragging')
|
|
166
|
+
return
|
|
118
167
|
}
|
|
168
|
+
// Otherwise the composer trails the pointer.
|
|
169
|
+
const point = editor.inputs.getCurrentPagePoint()
|
|
170
|
+
pendingComment.update(editor, (p) => (p ? { ...p, point: { x: point.x, y: point.y } } : p))
|
|
119
171
|
}
|
|
120
172
|
|
|
121
|
-
//
|
|
173
|
+
// Settle where the pointer is released: anchor to the shape under it, or drop a point.
|
|
122
174
|
override onPointerUp() {
|
|
123
175
|
const { editor } = this
|
|
124
176
|
const point = editor.inputs.getCurrentPagePoint()
|
|
125
177
|
const hit = editor.getShapeAtPoint(point, { hitInside: true })
|
|
126
178
|
const anchor: TLCommentAnchor = hit
|
|
127
|
-
? shapeAnchorAt(
|
|
179
|
+
? shapeAnchorAt(
|
|
180
|
+
editor,
|
|
181
|
+
hit.id,
|
|
182
|
+
point,
|
|
183
|
+
getCommentingOptions(editor).shouldBePrecise(editor, {
|
|
184
|
+
shapeId: hit.id,
|
|
185
|
+
point,
|
|
186
|
+
altKey: editor.inputs.getAltKey(),
|
|
187
|
+
})
|
|
188
|
+
)
|
|
128
189
|
: { type: 'point', x: point.x, y: point.y }
|
|
129
190
|
pendingComment.set(editor, { anchor, point: { x: point.x, y: point.y } })
|
|
130
191
|
// Hand back to select; the open composer is now the focus.
|
|
131
192
|
editor.setCurrentTool('select')
|
|
132
193
|
}
|
|
194
|
+
|
|
195
|
+
override onCancel() {
|
|
196
|
+
this.cancel()
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
override onInterrupt() {
|
|
200
|
+
this.cancel()
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Abandon the follow composer if placement is interrupted (Escape, focus loss, etc.).
|
|
204
|
+
private cancel() {
|
|
205
|
+
pendingComment.set(this.editor, null)
|
|
206
|
+
this.editor.setCurrentTool('select')
|
|
207
|
+
}
|
|
133
208
|
}
|
|
134
209
|
|
|
135
210
|
class CommentDragging extends StateNode {
|
|
136
211
|
static override id = 'dragging'
|
|
137
212
|
|
|
138
213
|
override onEnter() {
|
|
214
|
+
// A region drag supersedes the point-follow composer opened in `pointing`. Show a crosshair
|
|
215
|
+
// again — the composer no longer stands in for the pointer, so a hidden cursor would leave
|
|
216
|
+
// the drag with no pointer at all.
|
|
217
|
+
pendingComment.set(this.editor, null)
|
|
218
|
+
this.editor.setCursor({ type: 'cross', rotation: 0 })
|
|
139
219
|
this.updateDraft()
|
|
140
220
|
}
|
|
141
221
|
|
|
@@ -146,14 +226,15 @@ class CommentDragging extends StateNode {
|
|
|
146
226
|
// Commit the dragged rectangle as a region anchor; the composer opens at its pin corner.
|
|
147
227
|
override onPointerUp() {
|
|
148
228
|
const { editor } = this
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
229
|
+
const origin = editor.inputs.getOriginPagePoint()
|
|
230
|
+
const current = editor.inputs.getCurrentPagePoint()
|
|
231
|
+
const region = regionBetween(origin, current)
|
|
232
|
+
// The pin lives on the corner the drag released on — drag up-left, pin top-left.
|
|
233
|
+
const pin = { x: current.x >= origin.x ? 1 : 0, y: current.y >= origin.y ? 1 : 0 }
|
|
153
234
|
regionDraft.set(editor, null)
|
|
154
235
|
pendingComment.set(editor, {
|
|
155
|
-
anchor: { type: 'region', ...region },
|
|
156
|
-
point: regionPinPoint(region,
|
|
236
|
+
anchor: { type: 'region', ...region, pinX: pin.x, pinY: pin.y },
|
|
237
|
+
point: regionPinPoint(region, pin),
|
|
157
238
|
})
|
|
158
239
|
editor.setCurrentTool('select')
|
|
159
240
|
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
useTranslation,
|
|
10
10
|
useValue,
|
|
11
11
|
} from 'tldraw'
|
|
12
|
+
import { TooltipButton } from '../ui/tooltip-button'
|
|
12
13
|
import { SidebarFilters } from './sidebar-filters'
|
|
13
14
|
import { sidebarFilters } from './state'
|
|
14
15
|
|
|
@@ -36,29 +37,29 @@ export function CommentsFilterMenu({
|
|
|
36
37
|
return (
|
|
37
38
|
<TldrawUiDropdownMenuRoot id="comments-filter">
|
|
38
39
|
<TldrawUiDropdownMenuTrigger>
|
|
39
|
-
<
|
|
40
|
-
type="button"
|
|
41
|
-
className="cmt-header-btn"
|
|
42
|
-
title={msg('comments.filter')}
|
|
43
|
-
aria-label={msg('comments.filter')}
|
|
44
|
-
>
|
|
40
|
+
<TooltipButton tooltip={msg('comments.filter')} className="tlui-cmt-header-btn">
|
|
45
41
|
<FilterIcon />
|
|
46
|
-
</
|
|
42
|
+
</TooltipButton>
|
|
47
43
|
</TldrawUiDropdownMenuTrigger>
|
|
48
|
-
<TldrawUiDropdownMenuContent
|
|
44
|
+
<TldrawUiDropdownMenuContent
|
|
45
|
+
className="tlui-cmt-menu"
|
|
46
|
+
side="bottom"
|
|
47
|
+
align="end"
|
|
48
|
+
alignOffset={0}
|
|
49
|
+
>
|
|
49
50
|
<TldrawUiMenuContextProvider type="menu" sourceId="menu">
|
|
50
51
|
<TldrawUiMenuGroup id="comments-filter">
|
|
51
52
|
<TldrawUiMenuCheckboxItem
|
|
52
|
-
id="show-
|
|
53
|
-
label="comments.show-
|
|
54
|
-
checked={filters.
|
|
55
|
-
onSelect={() => toggle('
|
|
53
|
+
id="show-all-pages"
|
|
54
|
+
label="comments.show-all-pages"
|
|
55
|
+
checked={!filters.onlyCurrentPage}
|
|
56
|
+
onSelect={() => toggle('onlyCurrentPage')}
|
|
56
57
|
/>
|
|
57
58
|
{canFilterByAuthor && (
|
|
58
59
|
<TldrawUiMenuCheckboxItem
|
|
59
|
-
id="
|
|
60
|
-
label="comments.
|
|
61
|
-
checked={filters.onlyMine}
|
|
60
|
+
id="show-all-threads"
|
|
61
|
+
label="comments.show-all-threads"
|
|
62
|
+
checked={!filters.onlyMine}
|
|
62
63
|
onSelect={() => toggle('onlyMine')}
|
|
63
64
|
/>
|
|
64
65
|
)}
|
|
@@ -71,10 +72,10 @@ export function CommentsFilterMenu({
|
|
|
71
72
|
/>
|
|
72
73
|
)}
|
|
73
74
|
<TldrawUiMenuCheckboxItem
|
|
74
|
-
id="
|
|
75
|
-
label="comments.
|
|
76
|
-
checked={filters.
|
|
77
|
-
onSelect={() => toggle('
|
|
75
|
+
id="show-resolved"
|
|
76
|
+
label="comments.show-resolved"
|
|
77
|
+
checked={filters.showResolved}
|
|
78
|
+
onSelect={() => toggle('showResolved')}
|
|
78
79
|
/>
|
|
79
80
|
</TldrawUiMenuGroup>
|
|
80
81
|
</TldrawUiMenuContextProvider>
|
|
@@ -85,20 +86,13 @@ export function CommentsFilterMenu({
|
|
|
85
86
|
|
|
86
87
|
function FilterIcon() {
|
|
87
88
|
return (
|
|
88
|
-
<svg
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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" />
|
|
89
|
+
<svg width="15" height="15" viewBox="0 0 15 15" fill="none" aria-hidden="true">
|
|
90
|
+
<path
|
|
91
|
+
d="M5.5 3C4.67157 3 4 3.67157 4 4.5C4 5.32843 4.67157 6 5.5 6C6.32843 6 7 5.32843 7 4.5C7 3.67157 6.32843 3 5.5 3ZM3 5C3.01671 5 3.03323 4.99918 3.04952 4.99758C3.28022 6.1399 4.28967 7 5.5 7C6.71033 7 7.71978 6.1399 7.95048 4.99758C7.96677 4.99918 7.98329 5 8 5H13.5C13.7761 5 14 4.77614 14 4.5C14 4.22386 13.7761 4 13.5 4H8C7.98329 4 7.96677 4.00082 7.95048 4.00242C7.71978 2.86009 6.71033 2 5.5 2C4.28967 2 3.28022 2.86009 3.04952 4.00242C3.03323 4.00082 3.01671 4 3 4H1.5C1.22386 4 1 4.22386 1 4.5C1 4.77614 1.22386 5 1.5 5H3ZM11.9505 10.9976C11.7198 12.1399 10.7103 13 9.5 13C8.28967 13 7.28022 12.1399 7.04952 10.9976C7.03323 10.9992 7.01671 11 7 11H1.5C1.22386 11 1 10.7761 1 10.5C1 10.2239 1.22386 10 1.5 10H7C7.01671 10 7.03323 10.0008 7.04952 10.0024C7.28022 8.8601 8.28967 8 9.5 8C10.7103 8 11.7198 8.8601 11.9505 10.0024C11.9668 10.0008 11.9833 10 12 10H13.5C13.7761 10 14 10.2239 14 10.5C14 10.7761 13.7761 11 13.5 11H12C11.9833 11 11.9668 10.9992 11.9505 10.9976ZM8 10.5C8 9.67157 8.67157 9 9.5 9C10.3284 9 11 9.67157 11 10.5C11 11.3284 10.3284 12 9.5 12C8.67157 12 8 11.3284 8 10.5Z"
|
|
92
|
+
fill="currentColor"
|
|
93
|
+
fillRule="evenodd"
|
|
94
|
+
clipRule="evenodd"
|
|
95
|
+
/>
|
|
102
96
|
</svg>
|
|
103
97
|
)
|
|
104
98
|
}
|
|
@@ -4,10 +4,12 @@ import {
|
|
|
4
4
|
TldrawUiDropdownMenuItem,
|
|
5
5
|
TldrawUiDropdownMenuRoot,
|
|
6
6
|
TldrawUiDropdownMenuTrigger,
|
|
7
|
+
TldrawUiIcon,
|
|
7
8
|
useEditor,
|
|
8
9
|
useTranslation,
|
|
9
10
|
useValue,
|
|
10
11
|
} from 'tldraw'
|
|
12
|
+
import { TooltipButton } from '../ui/tooltip-button'
|
|
11
13
|
import { commentsHidden, toggleCommentsHidden } from './state'
|
|
12
14
|
|
|
13
15
|
// A keyboard-shortcut glyph, not translatable copy — kept out of JSX as a constant.
|
|
@@ -24,25 +26,25 @@ export function CommentsOverflowMenu() {
|
|
|
24
26
|
return (
|
|
25
27
|
<TldrawUiDropdownMenuRoot id="comments-overflow">
|
|
26
28
|
<TldrawUiDropdownMenuTrigger>
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
title={msg('comments.more-options')}
|
|
31
|
-
aria-label={msg('comments.more-options')}
|
|
32
|
-
>
|
|
33
|
-
<MoreIcon />
|
|
34
|
-
</button>
|
|
29
|
+
<TooltipButton tooltip={msg('comments.more-options')} className="tlui-cmt-header-btn">
|
|
30
|
+
<TldrawUiIcon icon="dots-vertical" label={msg('comments.more-options')} small />
|
|
31
|
+
</TooltipButton>
|
|
35
32
|
</TldrawUiDropdownMenuTrigger>
|
|
36
|
-
<TldrawUiDropdownMenuContent
|
|
33
|
+
<TldrawUiDropdownMenuContent
|
|
34
|
+
className="tlui-cmt-menu"
|
|
35
|
+
side="bottom"
|
|
36
|
+
align="end"
|
|
37
|
+
alignOffset={0}
|
|
38
|
+
>
|
|
37
39
|
<TldrawUiDropdownMenuGroup>
|
|
38
40
|
<TldrawUiDropdownMenuItem>
|
|
39
41
|
<button
|
|
40
42
|
type="button"
|
|
41
|
-
className="cmt-menu-item"
|
|
43
|
+
className="tlui-cmt-menu-item"
|
|
42
44
|
onClick={() => toggleCommentsHidden(editor)}
|
|
43
45
|
>
|
|
44
46
|
<span>{hidden ? msg('comments.show') : msg('comments.hide')}</span>
|
|
45
|
-
<span className="cmt-menu-item__shortcut">{HIDE_SHORTCUT}</span>
|
|
47
|
+
<span className="tlui-cmt-menu-item__shortcut">{HIDE_SHORTCUT}</span>
|
|
46
48
|
</button>
|
|
47
49
|
</TldrawUiDropdownMenuItem>
|
|
48
50
|
</TldrawUiDropdownMenuGroup>
|
|
@@ -50,13 +52,3 @@ export function CommentsOverflowMenu() {
|
|
|
50
52
|
</TldrawUiDropdownMenuRoot>
|
|
51
53
|
)
|
|
52
54
|
}
|
|
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
|
-
}
|