@tldraw/commenting 0.0.0-bootstrap → 5.3.0-canary.31036673d3f8
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 +0 -2
- package/commenting.css +1139 -329
- package/dist-cjs/canvas/anchor-lifecycle.js +148 -0
- package/dist-cjs/canvas/anchor-lifecycle.js.map +7 -0
- package/dist-cjs/canvas/cluster-input.js +2 -2
- package/dist-cjs/canvas/cluster-input.js.map +2 -2
- 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-mutations.js +123 -0
- package/dist-cjs/canvas/comment-mutations.js.map +7 -0
- package/dist-cjs/canvas/comment-reactions.js +163 -0
- package/dist-cjs/canvas/comment-reactions.js.map +7 -0
- package/dist-cjs/canvas/comment-render.js +5 -3
- package/dist-cjs/canvas/comment-render.js.map +2 -2
- package/dist-cjs/canvas/comment-store.js +19 -9
- package/dist-cjs/canvas/comment-store.js.map +2 -2
- package/dist-cjs/canvas/comment-tool.js +64 -14
- package/dist-cjs/canvas/comment-tool.js.map +2 -2
- package/dist-cjs/canvas/comments-filter-menu.js +20 -38
- package/dist-cjs/canvas/comments-filter-menu.js.map +2 -2
- package/dist-cjs/canvas/comments-overflow-menu.js +22 -27
- package/dist-cjs/canvas/comments-overflow-menu.js.map +2 -2
- package/dist-cjs/canvas/comments-overlay.js +429 -474
- package/dist-cjs/canvas/comments-overlay.js.map +2 -2
- package/dist-cjs/canvas/comments-sidebar.js +35 -32
- package/dist-cjs/canvas/comments-sidebar.js.map +2 -2
- package/dist-cjs/canvas/comments-visibility-toggle.js +65 -0
- package/dist-cjs/canvas/comments-visibility-toggle.js.map +7 -0
- package/dist-cjs/canvas/context.js +17 -0
- package/dist-cjs/canvas/context.js.map +7 -0
- package/dist-cjs/canvas/hooks.js +22 -3
- package/dist-cjs/canvas/hooks.js.map +2 -2
- package/dist-cjs/canvas/options.js +19 -2
- package/dist-cjs/canvas/options.js.map +2 -2
- package/dist-cjs/canvas/pin-stacking.js +56 -0
- package/dist-cjs/canvas/pin-stacking.js.map +7 -0
- package/dist-cjs/canvas/sidebar-filters.js +2 -2
- package/dist-cjs/canvas/sidebar-filters.js.map +2 -2
- package/dist-cjs/canvas/state.js +28 -14
- package/dist-cjs/canvas/state.js.map +2 -2
- package/dist-cjs/canvas/thread-preview.js +185 -0
- package/dist-cjs/canvas/thread-preview.js.map +7 -0
- package/dist-cjs/canvas/thread-stack.js +199 -0
- package/dist-cjs/canvas/thread-stack.js.map +7 -0
- package/dist-cjs/canvas/thread-state.js +67 -21
- package/dist-cjs/canvas/thread-state.js.map +2 -2
- package/dist-cjs/canvas/thread-view.js +397 -0
- package/dist-cjs/canvas/thread-view.js.map +7 -0
- package/dist-cjs/clustering/computeClusterTable.js +4 -4
- package/dist-cjs/clustering/computeClusterTable.js.map +2 -2
- package/dist-cjs/clustering/runtime.js +119 -7
- package/dist-cjs/clustering/runtime.js.map +2 -2
- package/dist-cjs/clustering/types.js.map +1 -1
- package/dist-cjs/index.d.ts +763 -333
- package/dist-cjs/index.js +46 -28
- package/dist-cjs/index.js.map +2 -2
- package/dist-cjs/ui/byline.js +6 -4
- package/dist-cjs/ui/byline.js.map +2 -2
- package/dist-cjs/ui/comment-card.js +15 -8
- package/dist-cjs/ui/comment-card.js.map +2 -2
- package/dist-cjs/ui/comment-composer.js +137 -36
- package/dist-cjs/ui/comment-composer.js.map +2 -2
- package/dist-cjs/ui/comment-pin.js +24 -2
- package/dist-cjs/ui/comment-pin.js.map +2 -2
- package/dist-cjs/ui/comment-thread.js +9 -7
- package/dist-cjs/ui/comment-thread.js.map +2 -2
- package/dist-cjs/ui/comments-list.js +30 -18
- package/dist-cjs/ui/comments-list.js.map +2 -2
- package/dist-cjs/ui/count-badge.js +3 -2
- package/dist-cjs/ui/count-badge.js.map +2 -2
- package/dist-cjs/ui/emoji-picker.js +54 -0
- package/dist-cjs/ui/emoji-picker.js.map +7 -0
- 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 +4 -1
- package/dist-cjs/ui/format-time.js.map +2 -2
- package/dist-cjs/ui/reaction-picker.js +73 -0
- package/dist-cjs/ui/reaction-picker.js.map +7 -0
- package/dist-cjs/ui/reaction.js +59 -6
- package/dist-cjs/ui/reaction.js.map +2 -2
- package/dist-cjs/ui/reactions.js +20 -7
- package/dist-cjs/ui/reactions.js.map +2 -2
- package/dist-cjs/ui/{mention.js → reply-count.js} +9 -11
- package/dist-cjs/ui/reply-count.js.map +7 -0
- package/dist-cjs/ui/send-button.js +20 -1
- package/dist-cjs/ui/send-button.js.map +2 -2
- package/dist-cjs/ui/{comment-text.js → tooltip-button.js} +12 -9
- package/dist-cjs/ui/tooltip-button.js.map +7 -0
- package/dist-esm/canvas/anchor-lifecycle.mjs +128 -0
- package/dist-esm/canvas/anchor-lifecycle.mjs.map +7 -0
- package/dist-esm/canvas/cluster-input.mjs +2 -2
- package/dist-esm/canvas/cluster-input.mjs.map +2 -2
- package/dist-esm/canvas/comment-body.mjs +1 -1
- package/dist-esm/canvas/comment-body.mjs.map +2 -2
- package/dist-esm/canvas/comment-drafts.mjs +34 -0
- package/dist-esm/canvas/comment-drafts.mjs.map +7 -0
- package/dist-esm/canvas/comment-mutations.mjs +103 -0
- package/dist-esm/canvas/comment-mutations.mjs.map +7 -0
- package/dist-esm/canvas/comment-reactions.mjs +152 -0
- package/dist-esm/canvas/comment-reactions.mjs.map +7 -0
- package/dist-esm/canvas/comment-render.mjs +5 -3
- package/dist-esm/canvas/comment-render.mjs.map +2 -2
- package/dist-esm/canvas/comment-store.mjs +19 -9
- package/dist-esm/canvas/comment-store.mjs.map +2 -2
- package/dist-esm/canvas/comment-tool.mjs +67 -17
- package/dist-esm/canvas/comment-tool.mjs.map +2 -2
- package/dist-esm/canvas/comments-filter-menu.mjs +20 -38
- package/dist-esm/canvas/comments-filter-menu.mjs.map +2 -2
- package/dist-esm/canvas/comments-overflow-menu.mjs +23 -27
- package/dist-esm/canvas/comments-overflow-menu.mjs.map +2 -2
- package/dist-esm/canvas/comments-overlay.mjs +450 -479
- package/dist-esm/canvas/comments-overlay.mjs.map +3 -3
- package/dist-esm/canvas/comments-sidebar.mjs +38 -35
- package/dist-esm/canvas/comments-sidebar.mjs.map +2 -2
- package/dist-esm/canvas/comments-visibility-toggle.mjs +45 -0
- package/dist-esm/canvas/comments-visibility-toggle.mjs.map +7 -0
- package/dist-esm/canvas/context.mjs +1 -0
- package/dist-esm/canvas/context.mjs.map +7 -0
- package/dist-esm/canvas/hooks.mjs +28 -5
- package/dist-esm/canvas/hooks.mjs.map +2 -2
- package/dist-esm/canvas/options.mjs +21 -3
- package/dist-esm/canvas/options.mjs.map +2 -2
- package/dist-esm/canvas/pin-stacking.mjs +36 -0
- package/dist-esm/canvas/pin-stacking.mjs.map +7 -0
- package/dist-esm/canvas/sidebar-filters.mjs +2 -2
- package/dist-esm/canvas/sidebar-filters.mjs.map +2 -2
- package/dist-esm/canvas/state.mjs +29 -19
- package/dist-esm/canvas/state.mjs.map +2 -2
- package/dist-esm/canvas/thread-preview.mjs +170 -0
- package/dist-esm/canvas/thread-preview.mjs.map +7 -0
- package/dist-esm/canvas/thread-stack.mjs +190 -0
- package/dist-esm/canvas/thread-stack.mjs.map +7 -0
- package/dist-esm/canvas/thread-state.mjs +71 -22
- package/dist-esm/canvas/thread-state.mjs.map +2 -2
- package/dist-esm/canvas/thread-view.mjs +402 -0
- package/dist-esm/canvas/thread-view.mjs.map +7 -0
- package/dist-esm/clustering/computeClusterTable.mjs +4 -4
- package/dist-esm/clustering/computeClusterTable.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 +763 -333
- package/dist-esm/index.mjs +87 -41
- package/dist-esm/index.mjs.map +2 -2
- package/dist-esm/ui/byline.mjs +6 -4
- package/dist-esm/ui/byline.mjs.map +2 -2
- package/dist-esm/ui/comment-card.mjs +15 -8
- package/dist-esm/ui/comment-card.mjs.map +2 -2
- package/dist-esm/ui/comment-composer.mjs +148 -36
- package/dist-esm/ui/comment-composer.mjs.map +2 -2
- package/dist-esm/ui/comment-pin.mjs +24 -2
- package/dist-esm/ui/comment-pin.mjs.map +2 -2
- package/dist-esm/ui/comment-thread.mjs +9 -7
- package/dist-esm/ui/comment-thread.mjs.map +2 -2
- package/dist-esm/ui/comments-list.mjs +43 -29
- package/dist-esm/ui/comments-list.mjs.map +2 -2
- package/dist-esm/ui/count-badge.mjs +3 -2
- package/dist-esm/ui/count-badge.mjs.map +2 -2
- package/dist-esm/ui/emoji-picker.mjs +34 -0
- package/dist-esm/ui/emoji-picker.mjs.map +7 -0
- 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 +4 -1
- package/dist-esm/ui/format-time.mjs.map +2 -2
- package/dist-esm/ui/reaction-picker.mjs +60 -0
- package/dist-esm/ui/reaction-picker.mjs.map +7 -0
- package/dist-esm/ui/reaction.mjs +60 -7
- package/dist-esm/ui/reaction.mjs.map +2 -2
- package/dist-esm/ui/reactions.mjs +21 -8
- package/dist-esm/ui/reactions.mjs.map +2 -2
- package/dist-esm/ui/reply-count.mjs +9 -0
- package/dist-esm/ui/reply-count.mjs.map +7 -0
- package/dist-esm/ui/send-button.mjs +20 -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 +6 -6
- package/src/canvas/anchor-lifecycle.test.ts +290 -0
- package/src/canvas/anchor-lifecycle.ts +205 -0
- package/src/canvas/canvas.css +461 -46
- package/src/canvas/cluster-input.test.ts +31 -23
- package/src/canvas/cluster-input.ts +3 -4
- 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-mutations.test.ts +410 -0
- package/src/canvas/comment-mutations.ts +288 -0
- package/src/canvas/comment-reactions.test.ts +132 -0
- package/src/canvas/comment-reactions.tsx +245 -0
- package/src/canvas/comment-render.test.ts +1 -1
- package/src/canvas/comment-render.ts +9 -5
- package/src/canvas/comment-store.ts +67 -27
- package/src/canvas/comment-tool.test.ts +162 -0
- package/src/canvas/comment-tool.tsx +105 -23
- package/src/canvas/comments-filter-menu.tsx +23 -32
- package/src/canvas/comments-overflow-menu.tsx +14 -21
- package/src/canvas/comments-overlay.tsx +695 -622
- package/src/canvas/comments-sidebar.test.ts +45 -0
- package/src/canvas/comments-sidebar.tsx +76 -58
- package/src/canvas/comments-visibility-toggle.tsx +49 -0
- package/src/canvas/context.ts +47 -0
- package/src/canvas/hooks.ts +52 -11
- package/src/canvas/options.test.ts +52 -2
- package/src/canvas/options.ts +133 -9
- package/src/canvas/pin-stacking.test.ts +114 -0
- package/src/canvas/pin-stacking.ts +57 -0
- package/src/canvas/sidebar-filters.ts +4 -3
- package/src/canvas/state.ts +92 -35
- package/src/canvas/thread-preview.test.ts +91 -0
- package/src/canvas/thread-preview.tsx +304 -0
- package/src/canvas/thread-stack.tsx +252 -0
- package/src/canvas/thread-state.test.ts +209 -0
- package/src/canvas/thread-state.ts +137 -36
- package/src/canvas/thread-view.tsx +516 -0
- package/src/clustering/computeClusterTable.test.ts +24 -23
- package/src/clustering/computeClusterTable.ts +5 -5
- package/src/clustering/runtime.test.ts +217 -7
- package/src/clustering/runtime.ts +171 -17
- package/src/clustering/types.ts +9 -9
- package/src/index.ts +76 -40
- package/src/ui/byline.tsx +7 -5
- package/src/ui/comment-card.tsx +20 -10
- package/src/ui/comment-composer.tsx +206 -40
- package/src/ui/comment-pin.tsx +26 -2
- package/src/ui/comment-thread.tsx +13 -6
- package/src/ui/comments-list.test.ts +31 -0
- package/src/ui/comments-list.tsx +45 -21
- package/src/ui/comments.css +566 -281
- package/src/ui/count-badge.tsx +9 -2
- package/src/ui/emoji-picker.test.ts +25 -0
- package/src/ui/emoji-picker.tsx +76 -0
- package/src/ui/empty-state.tsx +3 -3
- package/src/ui/format-time.ts +7 -2
- package/src/ui/reaction-picker.tsx +109 -0
- package/src/ui/reaction.tsx +121 -6
- package/src/ui/reactions.tsx +69 -10
- package/src/ui/reply-count.ts +16 -0
- package/src/ui/send-button.tsx +18 -3
- package/src/ui/tooltip-button.tsx +20 -0
- package/dist-cjs/canvas/region-options.js +0 -49
- package/dist-cjs/canvas/region-options.js.map +0 -7
- package/dist-cjs/ui/avatar.js +0 -43
- package/dist-cjs/ui/avatar.js.map +0 -7
- package/dist-cjs/ui/comment-mention.js +0 -42
- package/dist-cjs/ui/comment-mention.js.map +0 -7
- package/dist-cjs/ui/comment-text.js.map +0 -7
- package/dist-cjs/ui/mention-list.js +0 -70
- package/dist-cjs/ui/mention-list.js.map +0 -7
- package/dist-cjs/ui/mention-suggestion.js +0 -206
- package/dist-cjs/ui/mention-suggestion.js.map +0 -7
- package/dist-cjs/ui/mention.js.map +0 -7
- package/dist-cjs/ui/render-markdown.js +0 -63
- package/dist-cjs/ui/render-markdown.js.map +0 -7
- package/dist-esm/canvas/region-options.mjs +0 -29
- package/dist-esm/canvas/region-options.mjs.map +0 -7
- package/dist-esm/ui/avatar.mjs +0 -23
- package/dist-esm/ui/avatar.mjs.map +0 -7
- package/dist-esm/ui/comment-mention.mjs +0 -22
- package/dist-esm/ui/comment-mention.mjs.map +0 -7
- package/dist-esm/ui/comment-text.mjs +0 -9
- package/dist-esm/ui/comment-text.mjs.map +0 -7
- package/dist-esm/ui/mention-list.mjs +0 -50
- package/dist-esm/ui/mention-list.mjs.map +0 -7
- package/dist-esm/ui/mention-suggestion.mjs +0 -186
- package/dist-esm/ui/mention-suggestion.mjs.map +0 -7
- package/dist-esm/ui/mention.mjs +0 -11
- package/dist-esm/ui/mention.mjs.map +0 -7
- package/dist-esm/ui/render-markdown.mjs +0 -45
- package/dist-esm/ui/render-markdown.mjs.map +0 -7
- package/src/canvas/region-options.ts +0 -57
- package/src/ui/avatar.tsx +0 -31
- package/src/ui/comment-mention.ts +0 -47
- package/src/ui/comment-text.tsx +0 -12
- package/src/ui/mention-list.tsx +0 -106
- package/src/ui/mention-suggestion.test.ts +0 -18
- package/src/ui/mention-suggestion.tsx +0 -285
- package/src/ui/mention.tsx +0 -9
- package/src/ui/render-markdown.tsx +0 -72
package/src/index.ts
CHANGED
|
@@ -1,58 +1,93 @@
|
|
|
1
1
|
import { registerTldrawLibraryVersion } from '@tldraw/utils'
|
|
2
2
|
|
|
3
3
|
// Presentational commenting components. These are tldraw-independent and can be used to build
|
|
4
|
-
// custom commenting UI.
|
|
5
|
-
|
|
4
|
+
// custom commenting UI. The mention picker, pill, and avatar are re-exported from @tldraw/mentions
|
|
5
|
+
// so the commenting public API is unchanged.
|
|
6
|
+
export {
|
|
7
|
+
Avatar,
|
|
8
|
+
type AvatarProps,
|
|
9
|
+
type CommentAuthor,
|
|
10
|
+
createMentionSuggestion,
|
|
11
|
+
filterMentionMembers,
|
|
12
|
+
Mention,
|
|
13
|
+
MentionList,
|
|
14
|
+
type MentionListProps,
|
|
15
|
+
type MentionMember,
|
|
16
|
+
type MentionProps,
|
|
17
|
+
type MentionSuggestionOptions,
|
|
18
|
+
} from '@tldraw/mentions'
|
|
6
19
|
export { Byline, type BylineProps } from './ui/byline'
|
|
7
20
|
export { CommentCard, type CommentCardProps } from './ui/comment-card'
|
|
8
21
|
export { CommentComposer, type CommentComposerProps } from './ui/comment-composer'
|
|
9
22
|
export { CountBadge, type CountBadgeProps } from './ui/count-badge'
|
|
10
23
|
export { CommentPin, type CommentPinProps } from './ui/comment-pin'
|
|
11
|
-
export { CommentText, type CommentTextProps } from './ui/comment-text'
|
|
12
24
|
export { CommentThread, type CommentThreadProps } from './ui/comment-thread'
|
|
13
|
-
export {
|
|
25
|
+
export {
|
|
26
|
+
CommentsList,
|
|
27
|
+
isOpenInNewTabClick,
|
|
28
|
+
type CommentListItemProps,
|
|
29
|
+
type CommentsListProps,
|
|
30
|
+
} from './ui/comments-list'
|
|
14
31
|
export { EmptyState, type EmptyStateProps } from './ui/empty-state'
|
|
15
32
|
export { formatRelativeTime } from './ui/format-time'
|
|
16
|
-
export { Mention, type MentionProps } from './ui/mention'
|
|
17
|
-
export { MentionList, type MentionListProps, type MentionMember } from './ui/mention-list'
|
|
18
33
|
export {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
type
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
export {
|
|
25
|
-
|
|
34
|
+
DEFAULT_REACTION_EMOJI,
|
|
35
|
+
EmojiPicker,
|
|
36
|
+
type EmojiPickerProps,
|
|
37
|
+
isAllowedReactionEmoji,
|
|
38
|
+
} from './ui/emoji-picker'
|
|
39
|
+
export {
|
|
40
|
+
Reaction,
|
|
41
|
+
type ReactionProps,
|
|
42
|
+
type RenderReaction,
|
|
43
|
+
defaultRenderReaction,
|
|
44
|
+
DefaultReactionTooltip,
|
|
45
|
+
DefaultReactionTooltipContent,
|
|
46
|
+
type ReactionTooltipProps,
|
|
47
|
+
} from './ui/reaction'
|
|
48
|
+
export { ReactionPicker, type ReactionPickerProps } from './ui/reaction-picker'
|
|
49
|
+
export {
|
|
50
|
+
Reactions,
|
|
51
|
+
type ReactionReactor,
|
|
52
|
+
type ReactionsProps,
|
|
53
|
+
type ReactionSummary,
|
|
54
|
+
} from './ui/reactions'
|
|
26
55
|
export { SendButton, type SendButtonProps } from './ui/send-button'
|
|
27
56
|
|
|
28
57
|
// The tldraw-coupled commenting layer: the comment tool, reactive hooks over the comment
|
|
29
58
|
// records, a rich-text body renderer, and a batteries-included <CanvasComments> overlay. Pairs
|
|
30
59
|
// with the presentational components above.
|
|
60
|
+
export { registerCommentAnchorLifecycle } from './canvas/anchor-lifecycle'
|
|
31
61
|
export { CommentBody, type CommentBodyProps } from './canvas/comment-body'
|
|
32
62
|
export {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
63
|
+
deleteComment,
|
|
64
|
+
deleteThread,
|
|
65
|
+
editComment,
|
|
66
|
+
putCommentRecords,
|
|
67
|
+
removeCommentRecords,
|
|
68
|
+
reopenThread,
|
|
69
|
+
resolveThread,
|
|
70
|
+
} from './canvas/comment-mutations'
|
|
71
|
+
export {
|
|
72
|
+
CommentReactionPicker,
|
|
73
|
+
type CommentReactionPickerProps,
|
|
74
|
+
CommentReactions,
|
|
75
|
+
type CommentReactionsProps,
|
|
76
|
+
summarizeReactions,
|
|
77
|
+
toggleCommentReaction,
|
|
78
|
+
useCommentReactions,
|
|
79
|
+
} from './canvas/comment-reactions'
|
|
80
|
+
export { CommentTool, commentToolOverrides, commentTools } from './canvas/comment-tool'
|
|
40
81
|
export {
|
|
82
|
+
getCommentReactions,
|
|
41
83
|
getCommentRecord,
|
|
42
84
|
getComments,
|
|
43
85
|
getCommentThreads,
|
|
44
|
-
|
|
45
|
-
|
|
86
|
+
getLiveComments,
|
|
87
|
+
getLiveCommentThreads,
|
|
46
88
|
type TLCommentRecord,
|
|
47
89
|
} from './canvas/comment-store'
|
|
48
|
-
export {
|
|
49
|
-
export type {
|
|
50
|
-
ClusterNode,
|
|
51
|
-
ClusterOptions,
|
|
52
|
-
ClusterTable,
|
|
53
|
-
LeafInput,
|
|
54
|
-
MergeEvent,
|
|
55
|
-
} from './clustering/types'
|
|
90
|
+
export { type CommentingContext } from './canvas/context'
|
|
56
91
|
export { CommentsFilterMenu, type CommentsFilterMenuProps } from './canvas/comments-filter-menu'
|
|
57
92
|
export { CommentsMenuItem } from './canvas/comments-menu-item'
|
|
58
93
|
export { CanvasComments, type CanvasCommentsProps } from './canvas/comments-overlay'
|
|
@@ -60,34 +95,35 @@ export {
|
|
|
60
95
|
type CommentingComponents,
|
|
61
96
|
type CommentingOptions,
|
|
62
97
|
defaultCommentingOptions,
|
|
98
|
+
getCanComment,
|
|
63
99
|
getCommentingOptions,
|
|
100
|
+
type ShapeCommentPrecisionContext,
|
|
101
|
+
useCanComment,
|
|
64
102
|
useCommentingOptions,
|
|
65
103
|
} from './canvas/options'
|
|
66
104
|
export { CommentsOverflowMenu } from './canvas/comments-overflow-menu'
|
|
105
|
+
export { CommentsVisibilityToggle } from './canvas/comments-visibility-toggle'
|
|
67
106
|
export { CanvasCommentsSidebar, type CanvasCommentsSidebarProps } from './canvas/comments-sidebar'
|
|
68
107
|
export { useComments, useCommentThreads, useThreadComments } from './canvas/hooks'
|
|
69
108
|
export { useCommentingEnabled } from './canvas/license'
|
|
70
|
-
export { DEFAULT_REGION_COMMENT_OPTIONS, type RegionCommentOptions } from './canvas/region-options'
|
|
71
109
|
export { richTextToPlaintext } from './canvas/rich-text'
|
|
72
110
|
export { DEFAULT_SIDEBAR_FILTERS, type SidebarFilters } from './canvas/sidebar-filters'
|
|
73
111
|
export {
|
|
74
112
|
commentsHidden,
|
|
75
|
-
|
|
113
|
+
commentsSidebarOpen,
|
|
114
|
+
getRevealThreadPending,
|
|
76
115
|
openThreadId,
|
|
77
|
-
|
|
116
|
+
revealThread,
|
|
78
117
|
sidebarFilters,
|
|
79
118
|
toggleCommentsHidden,
|
|
119
|
+
toggleCommentsSidebar,
|
|
80
120
|
useCommentsHidden,
|
|
121
|
+
useCommentsSidebarOpen,
|
|
81
122
|
useOpenThreadId,
|
|
82
|
-
|
|
123
|
+
useRevealThreadPending,
|
|
83
124
|
useSidebarFilters,
|
|
84
125
|
} from './canvas/state'
|
|
85
|
-
export {
|
|
86
|
-
anchorPagePoint,
|
|
87
|
-
DEFAULT_IMPRECISE_SHAPE_ANCHOR,
|
|
88
|
-
focusThread,
|
|
89
|
-
shapeAnchorAt,
|
|
90
|
-
} from './canvas/thread-state'
|
|
126
|
+
export { anchorPagePoint, focusThread, shapeAnchorAt } from './canvas/thread-state'
|
|
91
127
|
|
|
92
128
|
registerTldrawLibraryVersion(
|
|
93
129
|
(globalThis as any).TLDRAW_LIBRARY_NAME,
|
package/src/ui/byline.tsx
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { Avatar, type CommentAuthor } from '@tldraw/mentions'
|
|
1
2
|
import { formatRelativeTime } from './format-time'
|
|
2
3
|
|
|
3
4
|
/** @public */
|
|
4
5
|
export interface BylineProps {
|
|
5
|
-
author:
|
|
6
|
+
author: CommentAuthor
|
|
6
7
|
/** ISO datetime; formatted to relative time by the component. */
|
|
7
8
|
date: string
|
|
8
9
|
/** Shows an "edited" marker when the comment has been edited. */
|
|
@@ -12,11 +13,12 @@ export interface BylineProps {
|
|
|
12
13
|
/** A comment's metadata line: author name, relative time, and an edited marker. @public @react */
|
|
13
14
|
export function Byline({ author, date, edited }: BylineProps) {
|
|
14
15
|
return (
|
|
15
|
-
<div className="cmt-head">
|
|
16
|
-
<
|
|
17
|
-
<span className="cmt-
|
|
16
|
+
<div className="tlui-cmt-head">
|
|
17
|
+
<Avatar author={author} />
|
|
18
|
+
<span className="tlui-cmt-author">{author.name}</span>
|
|
19
|
+
<span className="tlui-cmt-time">
|
|
18
20
|
{formatRelativeTime(date)}
|
|
19
|
-
{edited && <span className="cmt-edited"> · edited</span>}
|
|
21
|
+
{edited && <span className="tlui-cmt-edited"> · edited</span>}
|
|
20
22
|
</span>
|
|
21
23
|
</div>
|
|
22
24
|
)
|
package/src/ui/comment-card.tsx
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import { type CommentAuthor } from '@tldraw/mentions'
|
|
1
2
|
import { ReactNode } from 'react'
|
|
2
|
-
import { Avatar } from './avatar'
|
|
3
3
|
import { Byline } from './byline'
|
|
4
4
|
|
|
5
5
|
/** @public */
|
|
6
6
|
export interface CommentCardProps {
|
|
7
|
-
author:
|
|
8
|
-
/** The rendered comment body. The card doesn't dictate a format — pass a `<
|
|
9
|
-
* for
|
|
7
|
+
author: CommentAuthor
|
|
8
|
+
/** The rendered comment body. The card doesn't dictate a format — pass a `<CommentBody>`
|
|
9
|
+
* for a comment's rich text, or any node of your own. */
|
|
10
10
|
body: ReactNode
|
|
11
11
|
/** ISO datetime; formatted to relative time by the component. */
|
|
12
12
|
date: string
|
|
@@ -15,18 +15,28 @@ export interface CommentCardProps {
|
|
|
15
15
|
edited?: boolean
|
|
16
16
|
/** Hover-revealed controls at the card's top-right (e.g. an edit affordance). */
|
|
17
17
|
actions?: ReactNode
|
|
18
|
+
/** Content under the body, aligned with it rather than the avatar (e.g. a `<Reactions>` row). */
|
|
19
|
+
footer?: ReactNode
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
/** A single comment: Avatar, Byline, and a body slot the consumer renders. @public @react */
|
|
21
|
-
export function CommentCard({
|
|
23
|
+
export function CommentCard({
|
|
24
|
+
author,
|
|
25
|
+
body,
|
|
26
|
+
date,
|
|
27
|
+
you,
|
|
28
|
+
edited,
|
|
29
|
+
actions,
|
|
30
|
+
footer,
|
|
31
|
+
}: CommentCardProps) {
|
|
22
32
|
return (
|
|
23
|
-
<div className={you ? 'cmt-card cmt-card--you' : 'cmt-card'}>
|
|
24
|
-
<
|
|
25
|
-
<div className="cmt-body">
|
|
26
|
-
<Byline author={author} date={date} edited={edited} />
|
|
33
|
+
<div className={you ? 'tlui-cmt-card tlui-cmt-card--you' : 'tlui-cmt-card'}>
|
|
34
|
+
<Byline author={author} date={date} edited={edited} />
|
|
35
|
+
<div className="tlui-cmt-body">
|
|
27
36
|
{body}
|
|
37
|
+
{footer}
|
|
28
38
|
</div>
|
|
29
|
-
{actions !== undefined && <div className="cmt-card__actions">{actions}</div>}
|
|
39
|
+
{actions !== undefined && <div className="tlui-cmt-card__actions">{actions}</div>}
|
|
30
40
|
</div>
|
|
31
41
|
)
|
|
32
42
|
}
|
|
@@ -1,17 +1,29 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { JSONContent } from '@tiptap/core'
|
|
2
2
|
import { EditorContent, useEditor } from '@tiptap/react'
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
Avatar,
|
|
5
|
+
type CommentAuthor,
|
|
6
|
+
createMentionExtension,
|
|
7
|
+
createMentionSuggestion,
|
|
8
|
+
isMentionPickerOpen,
|
|
9
|
+
MentionMember,
|
|
10
|
+
} from '@tldraw/mentions'
|
|
11
|
+
import {
|
|
12
|
+
type MouseEvent as ReactMouseEvent,
|
|
13
|
+
ReactNode,
|
|
14
|
+
useEffect,
|
|
15
|
+
useLayoutEffect,
|
|
16
|
+
useMemo,
|
|
17
|
+
useRef,
|
|
18
|
+
useState,
|
|
19
|
+
} from 'react'
|
|
4
20
|
import { isEqual, TLRichText, useMaybeEditor } from 'tldraw'
|
|
5
|
-
import { Avatar } from './avatar'
|
|
6
21
|
import { commentTipTapExtensions, EMPTY_COMMENT, isCommentEmpty } from './comment-extensions'
|
|
7
|
-
import { commentMention } from './comment-mention'
|
|
8
|
-
import { MentionMember } from './mention-list'
|
|
9
|
-
import { createMentionSuggestion, isMentionPickerOpen } from './mention-suggestion'
|
|
10
22
|
import { SendButton } from './send-button'
|
|
11
23
|
|
|
12
24
|
/** @public */
|
|
13
25
|
export interface CommentComposerProps {
|
|
14
|
-
author:
|
|
26
|
+
author: CommentAuthor
|
|
15
27
|
placeholder: string
|
|
16
28
|
/** Controlled rich-text value. Omit for the presentational (display-only) composer. */
|
|
17
29
|
value?: TLRichText
|
|
@@ -19,6 +31,9 @@ export interface CommentComposerProps {
|
|
|
19
31
|
/** Called on Send click or Enter. When set, the composer is interactive. */
|
|
20
32
|
onSubmit?(): void
|
|
21
33
|
sendLabel?: string
|
|
34
|
+
/** Called when Up is pressed in an empty composer — e.g. to start editing the comment above,
|
|
35
|
+
* the way chat apps edit your last message. With content in the field, Up moves the cursor. */
|
|
36
|
+
onArrowUpWhenEmpty?(): void
|
|
22
37
|
disabled?: boolean
|
|
23
38
|
autoFocus?: boolean
|
|
24
39
|
/** The leading element before the field. Defaults to the author's avatar. */
|
|
@@ -43,6 +58,7 @@ export function CommentComposer({
|
|
|
43
58
|
onChange,
|
|
44
59
|
onSubmit,
|
|
45
60
|
sendLabel = 'Send',
|
|
61
|
+
onArrowUpWhenEmpty,
|
|
46
62
|
disabled,
|
|
47
63
|
autoFocus,
|
|
48
64
|
leading,
|
|
@@ -60,6 +76,8 @@ export function CommentComposer({
|
|
|
60
76
|
onChangeRef.current = onChange
|
|
61
77
|
const onSubmitRef = useRef(onSubmit)
|
|
62
78
|
onSubmitRef.current = onSubmit
|
|
79
|
+
const onArrowUpWhenEmptyRef = useRef(onArrowUpWhenEmpty)
|
|
80
|
+
onArrowUpWhenEmptyRef.current = onArrowUpWhenEmpty
|
|
63
81
|
const disabledRef = useRef(disabled)
|
|
64
82
|
disabledRef.current = disabled
|
|
65
83
|
const getMentionSuggestionsRef = useRef(getMentionSuggestions)
|
|
@@ -68,29 +86,75 @@ export function CommentComposer({
|
|
|
68
86
|
renderMentionSuggestionRef.current = renderMentionSuggestion
|
|
69
87
|
|
|
70
88
|
const [isEmpty, setIsEmpty] = useState(() => !value || isCommentEmpty(value))
|
|
89
|
+
// Whether the field has grown to two rows: input across the full width, send button below.
|
|
90
|
+
// Flips when the (single-line) content width reaches the space left beside the send button.
|
|
91
|
+
const [expanded, setExpanded] = useState(false)
|
|
92
|
+
const expandedRef = useRef(expanded)
|
|
93
|
+
expandedRef.current = expanded
|
|
94
|
+
const inputWrapRef = useRef<HTMLDivElement>(null)
|
|
95
|
+
const mirrorRef = useRef<HTMLDivElement>(null)
|
|
71
96
|
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
97
|
+
// Measure whether the content still fits on one line beside the send button. The mirror is a
|
|
98
|
+
// hidden, nowrap clone of the editor's rendered content, so marks and mention chips measure at
|
|
99
|
+
// their true width. A small dead zone between the expand and collapse thresholds keeps the
|
|
100
|
+
// layout from flapping while typing at the boundary.
|
|
101
|
+
const remeasure = () => {
|
|
102
|
+
const wrap = inputWrapRef.current
|
|
103
|
+
const mirror = mirrorRef.current
|
|
104
|
+
const editor = editorRef.current
|
|
105
|
+
if (!wrap || !mirror || !editor) return
|
|
106
|
+
// Empty always fits — and measuring before TipTap's DOM has laid out reads zero widths,
|
|
107
|
+
// which would flash the expanded layout for a frame on mount.
|
|
108
|
+
if (editor.isEmpty) {
|
|
109
|
+
if (expandedRef.current) setExpanded(false)
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
if (wrap.clientWidth === 0) return
|
|
113
|
+
const doc = editor.state.doc
|
|
114
|
+
const multiBlock =
|
|
115
|
+
doc.childCount > 1 || (doc.firstChild !== null && doc.firstChild.type.name !== 'paragraph')
|
|
116
|
+
if (multiBlock) {
|
|
117
|
+
if (!expandedRef.current) setExpanded(true)
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
const field = wrap.parentElement
|
|
121
|
+
const send = field ? field.querySelector<HTMLElement>('.tlui-cmt-send') : null
|
|
122
|
+
const input = wrap.querySelector('.tlui-cmt-input')
|
|
123
|
+
if (!send || !input) return
|
|
124
|
+
mirror.innerHTML = input.innerHTML
|
|
125
|
+
const textWidth = mirror.offsetWidth
|
|
126
|
+
// The single-line space the input has beside the send button: the wrap's *content* box
|
|
127
|
+
// (clientWidth minus its own horizontal padding — the text wraps there, not at clientWidth),
|
|
128
|
+
// less the button plus the field's 6px gap when already expanded (the wrap then spans the
|
|
129
|
+
// full field). Without subtracting the padding, a padded wrap wraps to a second line ~padding
|
|
130
|
+
// px before expansion fires, so the input grows a line and then snaps to the expanded layout.
|
|
131
|
+
const wrapStyle = getComputedStyle(wrap)
|
|
132
|
+
const wrapPadX = parseFloat(wrapStyle.paddingLeft) + parseFloat(wrapStyle.paddingRight)
|
|
133
|
+
const collapsedAvailable =
|
|
134
|
+
wrap.clientWidth - wrapPadX - (expandedRef.current ? send.offsetWidth + 6 : 0)
|
|
135
|
+
if (!expandedRef.current && textWidth > collapsedAvailable - 8) {
|
|
136
|
+
setExpanded(true)
|
|
137
|
+
} else if (expandedRef.current && textWidth < collapsedAvailable - 24) {
|
|
138
|
+
setExpanded(false)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const remeasureRef = useRef(remeasure)
|
|
142
|
+
remeasureRef.current = remeasure
|
|
143
|
+
|
|
144
|
+
// The editor instance, reachable from `handleKeyDown` (which is created before `useEditor`
|
|
145
|
+
// returns). Updated on every render so the ref never points at a stale editor.
|
|
146
|
+
const editorRef = useRef<ReturnType<typeof useEditor>>(null)
|
|
147
|
+
|
|
148
|
+
// Set while we replay Enter through the keymaps for a Shift+Enter newline: `commands.enter()`
|
|
149
|
+
// re-dispatches Enter through `handleKeyDown`, and without this guard our own handler would catch
|
|
150
|
+
// that synthetic Enter and submit instead.
|
|
151
|
+
const replayingEnter = useRef(false)
|
|
152
|
+
|
|
153
|
+
// Enter and Cmd/Ctrl+Enter submit the comment; Shift+Enter inserts a new line for the occasional
|
|
154
|
+
// multi-line comment. This is handled through `editorProps.handleKeyDown` (below) rather than a
|
|
155
|
+
// keyboard-shortcut extension: ProseMirror runs `handleKeyDown` before every keymap plugin, so it
|
|
156
|
+
// can tell Shift+Enter apart from Enter — an `Enter` keymap binding also fires on Shift/Cmd+Enter
|
|
157
|
+
// and would otherwise swallow the newline.
|
|
94
158
|
|
|
95
159
|
// The suggestion plugin is built once (the editor is recreated only on `interactive`) and runs
|
|
96
160
|
// outside React, so it must read the mention callbacks through refs — like onChange/onSubmit —
|
|
@@ -100,7 +164,7 @@ export function CommentComposer({
|
|
|
100
164
|
const mentionsEnabled = !!getMentionSuggestions
|
|
101
165
|
const hasCustomRow = !!renderMentionSuggestion
|
|
102
166
|
const extensions = useMemo(() => {
|
|
103
|
-
const list = [...commentTipTapExtensions
|
|
167
|
+
const list = [...commentTipTapExtensions]
|
|
104
168
|
// Always register the mention node so an existing body that contains a mention keeps it on
|
|
105
169
|
// edit (an unregistered node would be stripped by ProseMirror when the content loads). The `@`
|
|
106
170
|
// picker itself only turns on when the host provides a resolver; otherwise the node is present
|
|
@@ -111,7 +175,7 @@ export function CommentComposer({
|
|
|
111
175
|
? (member: MentionMember) => renderMentionSuggestionRef.current?.(member)
|
|
112
176
|
: undefined
|
|
113
177
|
list.push(
|
|
114
|
-
|
|
178
|
+
createMentionExtension({
|
|
115
179
|
suggestion: createMentionSuggestion(resolveSuggestions, {
|
|
116
180
|
renderMember: renderRow,
|
|
117
181
|
editor: tlEditor,
|
|
@@ -119,10 +183,10 @@ export function CommentComposer({
|
|
|
119
183
|
})
|
|
120
184
|
)
|
|
121
185
|
} else {
|
|
122
|
-
list.push(
|
|
186
|
+
list.push(createMentionExtension({ suggestion: { char: '@', allow: () => false } }))
|
|
123
187
|
}
|
|
124
188
|
return list
|
|
125
|
-
}, [
|
|
189
|
+
}, [mentionsEnabled, hasCustomRow, tlEditor])
|
|
126
190
|
|
|
127
191
|
const editor = useEditor(
|
|
128
192
|
{
|
|
@@ -134,14 +198,65 @@ export function CommentComposer({
|
|
|
134
198
|
// mirrors RichTextArea's setup.
|
|
135
199
|
enableCoreExtensions: { textDirection: false },
|
|
136
200
|
textDirection: 'auto',
|
|
137
|
-
editorProps: {
|
|
201
|
+
editorProps: {
|
|
202
|
+
attributes: {
|
|
203
|
+
class: 'tlui-cmt-input',
|
|
204
|
+
'aria-label': placeholder,
|
|
205
|
+
role: 'textbox',
|
|
206
|
+
'aria-multiline': 'true',
|
|
207
|
+
},
|
|
208
|
+
// Runs before every keymap plugin, so it can distinguish Shift+Enter from Enter — an
|
|
209
|
+
// `Enter` keymap binding also fires on Shift+Enter and would otherwise swallow it.
|
|
210
|
+
handleKeyDown: (_view, event) => {
|
|
211
|
+
// Let the keymaps handle the synthetic Enter we replay for a Shift+Enter newline.
|
|
212
|
+
if (replayingEnter.current) return false
|
|
213
|
+
// Up in an empty composer hands off to the host (edit the comment above). With
|
|
214
|
+
// content, Up stays cursor movement; with the mention picker open, it navigates it.
|
|
215
|
+
if (event.key === 'ArrowUp' && !event.isComposing) {
|
|
216
|
+
if (
|
|
217
|
+
onArrowUpWhenEmptyRef.current &&
|
|
218
|
+
editorRef.current?.isEmpty &&
|
|
219
|
+
!isMentionPickerOpen()
|
|
220
|
+
) {
|
|
221
|
+
onArrowUpWhenEmptyRef.current()
|
|
222
|
+
return true
|
|
223
|
+
}
|
|
224
|
+
return false
|
|
225
|
+
}
|
|
226
|
+
if (event.key !== 'Enter' || event.isComposing) return false
|
|
227
|
+
// While the @-mention picker is open, Enter selects the highlighted member — defer.
|
|
228
|
+
if (isMentionPickerOpen()) return false
|
|
229
|
+
// Shift+Enter inserts a new line. Replay a plain Enter through the keymaps (guarded so
|
|
230
|
+
// we don't re-enter and submit) to reuse the editor's list-aware Enter handling — a new
|
|
231
|
+
// list item in a list, a new paragraph otherwise. tldraw doesn't do soft breaks.
|
|
232
|
+
if (event.shiftKey && !event.metaKey && !event.ctrlKey && !event.altKey) {
|
|
233
|
+
// An empty field has nothing to break onto — swallow the keypress so it doesn't
|
|
234
|
+
// open the comment with a stray leading blank line.
|
|
235
|
+
if (editorRef.current?.isEmpty) return true
|
|
236
|
+
replayingEnter.current = true
|
|
237
|
+
try {
|
|
238
|
+
editorRef.current?.commands.enter()
|
|
239
|
+
} finally {
|
|
240
|
+
replayingEnter.current = false
|
|
241
|
+
}
|
|
242
|
+
return true
|
|
243
|
+
}
|
|
244
|
+
// Enter, Cmd+Enter, and Ctrl+Enter submit.
|
|
245
|
+
if (!disabledRef.current) onSubmitRef.current?.()
|
|
246
|
+
return true
|
|
247
|
+
},
|
|
248
|
+
},
|
|
138
249
|
onUpdate: ({ editor }) => {
|
|
139
250
|
setIsEmpty(editor.isEmpty)
|
|
251
|
+
// Same-value state sets don't re-render, so measure here — the editor's DOM is
|
|
252
|
+
// already updated when onUpdate fires.
|
|
253
|
+
remeasureRef.current()
|
|
140
254
|
onChangeRef.current?.(editor.getJSON() as TLRichText)
|
|
141
255
|
},
|
|
142
256
|
},
|
|
143
|
-
[interactive]
|
|
257
|
+
[interactive, placeholder]
|
|
144
258
|
)
|
|
259
|
+
editorRef.current = editor
|
|
145
260
|
|
|
146
261
|
// Sync controlled resets (e.g. the parent clearing to EMPTY_COMMENT after posting) into the
|
|
147
262
|
// editor without echoing back the value the editor itself just emitted.
|
|
@@ -152,6 +267,12 @@ export function CommentComposer({
|
|
|
152
267
|
setIsEmpty(editor.isEmpty)
|
|
153
268
|
}, [editor, value])
|
|
154
269
|
|
|
270
|
+
// Measure once the editor's content is in the DOM, so a composer that mounts pre-filled (the
|
|
271
|
+
// edit-in-place composer) starts in the right layout.
|
|
272
|
+
useLayoutEffect(() => {
|
|
273
|
+
remeasureRef.current()
|
|
274
|
+
}, [editor, value])
|
|
275
|
+
|
|
155
276
|
// Focus on the next frame rather than via TipTap's autofocus: the composer often mounts from a
|
|
156
277
|
// canvas pointer event whose default focus handling would otherwise steal it back.
|
|
157
278
|
useEffect(() => {
|
|
@@ -160,17 +281,62 @@ export function CommentComposer({
|
|
|
160
281
|
return () => cancelAnimationFrame(raf)
|
|
161
282
|
}, [autoFocus, editor])
|
|
162
283
|
|
|
284
|
+
// tldraw's pass-through-wheel hook (on the floating composer's wrapper) forwards a wheel to the
|
|
285
|
+
// canvas unless ITS element is scrollable — but the scrollable element is this input, not that
|
|
286
|
+
// wrapper, so an overflowing input never scrolled: the wheel panned the canvas instead. Catch the
|
|
287
|
+
// wheel on the wrap first (a native listener here fires before the wrapper's, deeper in the tree)
|
|
288
|
+
// and stop it while the input can still scroll that way; at the scroll boundary let it through so
|
|
289
|
+
// the canvas still zooms.
|
|
290
|
+
useEffect(() => {
|
|
291
|
+
const wrap = inputWrapRef.current
|
|
292
|
+
if (!wrap) return
|
|
293
|
+
const onWheel = (e: WheelEvent) => {
|
|
294
|
+
const input = wrap.querySelector<HTMLElement>('.tlui-cmt-input')
|
|
295
|
+
if (!input || input.scrollHeight <= input.clientHeight) return
|
|
296
|
+
const atTop = input.scrollTop <= 0
|
|
297
|
+
const atBottom = input.scrollTop >= input.scrollHeight - input.clientHeight - 1
|
|
298
|
+
if ((e.deltaY < 0 && !atTop) || (e.deltaY > 0 && !atBottom)) {
|
|
299
|
+
e.stopPropagation()
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
wrap.addEventListener('wheel', onWheel)
|
|
303
|
+
return () => wrap.removeEventListener('wheel', onWheel)
|
|
304
|
+
}, [])
|
|
305
|
+
|
|
306
|
+
// The whole field behaves like the text input: clicking its empty area (the padding, or the
|
|
307
|
+
// space beside/below a short line) focuses the editor rather than only the text glyphs being
|
|
308
|
+
// clickable. The input (caret placement) and the send button keep their own click handling.
|
|
309
|
+
const focusEditorFromField = (e: ReactMouseEvent<HTMLDivElement>) => {
|
|
310
|
+
const target = e.target as HTMLElement
|
|
311
|
+
if (target.closest('.tlui-cmt-input') || target.closest('.tlui-cmt-send')) return
|
|
312
|
+
e.preventDefault()
|
|
313
|
+
editor?.commands.focus('end')
|
|
314
|
+
}
|
|
315
|
+
|
|
163
316
|
return (
|
|
164
|
-
<div className="cmt-composer">
|
|
165
|
-
{leading ?? <Avatar
|
|
166
|
-
<div
|
|
167
|
-
|
|
317
|
+
<div className="tlui-cmt-composer">
|
|
318
|
+
{leading ?? <Avatar author={author} />}
|
|
319
|
+
<div
|
|
320
|
+
className={[
|
|
321
|
+
'tlui-cmt-composer__field',
|
|
322
|
+
interactive && expanded && 'tlui-cmt-composer__field--expanded',
|
|
323
|
+
]
|
|
324
|
+
.filter(Boolean)
|
|
325
|
+
.join(' ')}
|
|
326
|
+
onMouseDown={interactive ? focusEditorFromField : undefined}
|
|
327
|
+
>
|
|
328
|
+
<div className="tlui-cmt-composer__input-wrap" ref={inputWrapRef}>
|
|
168
329
|
<EditorContent editor={editor} />
|
|
169
330
|
{isEmpty && (
|
|
170
|
-
<div className="cmt-input__placeholder" aria-hidden="true">
|
|
331
|
+
<div className="tlui-cmt-input__placeholder" aria-hidden="true">
|
|
171
332
|
{placeholder}
|
|
172
333
|
</div>
|
|
173
334
|
)}
|
|
335
|
+
<div
|
|
336
|
+
className="tlui-cmt-composer__mirror tlui-cmt-input"
|
|
337
|
+
ref={mirrorRef}
|
|
338
|
+
aria-hidden="true"
|
|
339
|
+
/>
|
|
174
340
|
</div>
|
|
175
341
|
{interactive && <SendButton label={sendLabel} onClick={onSubmit} disabled={disabled} />}
|
|
176
342
|
</div>
|
package/src/ui/comment-pin.tsx
CHANGED
|
@@ -10,12 +10,36 @@ export interface CommentPinProps {
|
|
|
10
10
|
open?: boolean
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/* An inline check, not a text glyph — the '✓' character sits off-baseline and varies by font. */
|
|
14
|
+
const resolvedCheck = (
|
|
15
|
+
<svg
|
|
16
|
+
viewBox="0 0 24 24"
|
|
17
|
+
width="15"
|
|
18
|
+
height="15"
|
|
19
|
+
fill="none"
|
|
20
|
+
stroke="currentColor"
|
|
21
|
+
strokeWidth="2.5"
|
|
22
|
+
strokeLinecap="round"
|
|
23
|
+
strokeLinejoin="round"
|
|
24
|
+
aria-hidden="true"
|
|
25
|
+
>
|
|
26
|
+
<path d="M4 12.5l5 5L20 6.5" />
|
|
27
|
+
</svg>
|
|
28
|
+
)
|
|
29
|
+
|
|
13
30
|
/** A canvas comment marker: shows its `children` (or a check when resolved). Purely
|
|
14
31
|
* presentational — it reflects open/resolved state via CSS; wrap it to make it clickable.
|
|
15
32
|
* @public @react */
|
|
16
33
|
export function CommentPin({ children, resolved, open }: CommentPinProps) {
|
|
17
|
-
const className = [
|
|
34
|
+
const className = [
|
|
35
|
+
// `tlui-cmt-marker` carries the resting shadow and the hover lift — the same treatment a
|
|
36
|
+
// count badge wears, so the two markers behave alike.
|
|
37
|
+
'tlui-cmt-marker',
|
|
38
|
+
'tlui-cmt-pin',
|
|
39
|
+
resolved && 'tlui-cmt-pin--resolved',
|
|
40
|
+
open && 'tlui-cmt-marker--open',
|
|
41
|
+
]
|
|
18
42
|
.filter(Boolean)
|
|
19
43
|
.join(' ')
|
|
20
|
-
return <div className={className}>{resolved ?
|
|
44
|
+
return <div className={className}>{resolved ? resolvedCheck : children}</div>
|
|
21
45
|
}
|