@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.
Files changed (279) hide show
  1. package/README.md +0 -2
  2. package/commenting.css +1139 -329
  3. package/dist-cjs/canvas/anchor-lifecycle.js +148 -0
  4. package/dist-cjs/canvas/anchor-lifecycle.js.map +7 -0
  5. package/dist-cjs/canvas/cluster-input.js +2 -2
  6. package/dist-cjs/canvas/cluster-input.js.map +2 -2
  7. package/dist-cjs/canvas/comment-body.js +1 -1
  8. package/dist-cjs/canvas/comment-body.js.map +2 -2
  9. package/dist-cjs/canvas/comment-drafts.js +54 -0
  10. package/dist-cjs/canvas/comment-drafts.js.map +7 -0
  11. package/dist-cjs/canvas/comment-mutations.js +123 -0
  12. package/dist-cjs/canvas/comment-mutations.js.map +7 -0
  13. package/dist-cjs/canvas/comment-reactions.js +163 -0
  14. package/dist-cjs/canvas/comment-reactions.js.map +7 -0
  15. package/dist-cjs/canvas/comment-render.js +5 -3
  16. package/dist-cjs/canvas/comment-render.js.map +2 -2
  17. package/dist-cjs/canvas/comment-store.js +19 -9
  18. package/dist-cjs/canvas/comment-store.js.map +2 -2
  19. package/dist-cjs/canvas/comment-tool.js +64 -14
  20. package/dist-cjs/canvas/comment-tool.js.map +2 -2
  21. package/dist-cjs/canvas/comments-filter-menu.js +20 -38
  22. package/dist-cjs/canvas/comments-filter-menu.js.map +2 -2
  23. package/dist-cjs/canvas/comments-overflow-menu.js +22 -27
  24. package/dist-cjs/canvas/comments-overflow-menu.js.map +2 -2
  25. package/dist-cjs/canvas/comments-overlay.js +429 -474
  26. package/dist-cjs/canvas/comments-overlay.js.map +2 -2
  27. package/dist-cjs/canvas/comments-sidebar.js +35 -32
  28. package/dist-cjs/canvas/comments-sidebar.js.map +2 -2
  29. package/dist-cjs/canvas/comments-visibility-toggle.js +65 -0
  30. package/dist-cjs/canvas/comments-visibility-toggle.js.map +7 -0
  31. package/dist-cjs/canvas/context.js +17 -0
  32. package/dist-cjs/canvas/context.js.map +7 -0
  33. package/dist-cjs/canvas/hooks.js +22 -3
  34. package/dist-cjs/canvas/hooks.js.map +2 -2
  35. package/dist-cjs/canvas/options.js +19 -2
  36. package/dist-cjs/canvas/options.js.map +2 -2
  37. package/dist-cjs/canvas/pin-stacking.js +56 -0
  38. package/dist-cjs/canvas/pin-stacking.js.map +7 -0
  39. package/dist-cjs/canvas/sidebar-filters.js +2 -2
  40. package/dist-cjs/canvas/sidebar-filters.js.map +2 -2
  41. package/dist-cjs/canvas/state.js +28 -14
  42. package/dist-cjs/canvas/state.js.map +2 -2
  43. package/dist-cjs/canvas/thread-preview.js +185 -0
  44. package/dist-cjs/canvas/thread-preview.js.map +7 -0
  45. package/dist-cjs/canvas/thread-stack.js +199 -0
  46. package/dist-cjs/canvas/thread-stack.js.map +7 -0
  47. package/dist-cjs/canvas/thread-state.js +67 -21
  48. package/dist-cjs/canvas/thread-state.js.map +2 -2
  49. package/dist-cjs/canvas/thread-view.js +397 -0
  50. package/dist-cjs/canvas/thread-view.js.map +7 -0
  51. package/dist-cjs/clustering/computeClusterTable.js +4 -4
  52. package/dist-cjs/clustering/computeClusterTable.js.map +2 -2
  53. package/dist-cjs/clustering/runtime.js +119 -7
  54. package/dist-cjs/clustering/runtime.js.map +2 -2
  55. package/dist-cjs/clustering/types.js.map +1 -1
  56. package/dist-cjs/index.d.ts +763 -333
  57. package/dist-cjs/index.js +46 -28
  58. package/dist-cjs/index.js.map +2 -2
  59. package/dist-cjs/ui/byline.js +6 -4
  60. package/dist-cjs/ui/byline.js.map +2 -2
  61. package/dist-cjs/ui/comment-card.js +15 -8
  62. package/dist-cjs/ui/comment-card.js.map +2 -2
  63. package/dist-cjs/ui/comment-composer.js +137 -36
  64. package/dist-cjs/ui/comment-composer.js.map +2 -2
  65. package/dist-cjs/ui/comment-pin.js +24 -2
  66. package/dist-cjs/ui/comment-pin.js.map +2 -2
  67. package/dist-cjs/ui/comment-thread.js +9 -7
  68. package/dist-cjs/ui/comment-thread.js.map +2 -2
  69. package/dist-cjs/ui/comments-list.js +30 -18
  70. package/dist-cjs/ui/comments-list.js.map +2 -2
  71. package/dist-cjs/ui/count-badge.js +3 -2
  72. package/dist-cjs/ui/count-badge.js.map +2 -2
  73. package/dist-cjs/ui/emoji-picker.js +54 -0
  74. package/dist-cjs/ui/emoji-picker.js.map +7 -0
  75. package/dist-cjs/ui/empty-state.js +3 -3
  76. package/dist-cjs/ui/empty-state.js.map +2 -2
  77. package/dist-cjs/ui/format-time.js +4 -1
  78. package/dist-cjs/ui/format-time.js.map +2 -2
  79. package/dist-cjs/ui/reaction-picker.js +73 -0
  80. package/dist-cjs/ui/reaction-picker.js.map +7 -0
  81. package/dist-cjs/ui/reaction.js +59 -6
  82. package/dist-cjs/ui/reaction.js.map +2 -2
  83. package/dist-cjs/ui/reactions.js +20 -7
  84. package/dist-cjs/ui/reactions.js.map +2 -2
  85. package/dist-cjs/ui/{mention.js → reply-count.js} +9 -11
  86. package/dist-cjs/ui/reply-count.js.map +7 -0
  87. package/dist-cjs/ui/send-button.js +20 -1
  88. package/dist-cjs/ui/send-button.js.map +2 -2
  89. package/dist-cjs/ui/{comment-text.js → tooltip-button.js} +12 -9
  90. package/dist-cjs/ui/tooltip-button.js.map +7 -0
  91. package/dist-esm/canvas/anchor-lifecycle.mjs +128 -0
  92. package/dist-esm/canvas/anchor-lifecycle.mjs.map +7 -0
  93. package/dist-esm/canvas/cluster-input.mjs +2 -2
  94. package/dist-esm/canvas/cluster-input.mjs.map +2 -2
  95. package/dist-esm/canvas/comment-body.mjs +1 -1
  96. package/dist-esm/canvas/comment-body.mjs.map +2 -2
  97. package/dist-esm/canvas/comment-drafts.mjs +34 -0
  98. package/dist-esm/canvas/comment-drafts.mjs.map +7 -0
  99. package/dist-esm/canvas/comment-mutations.mjs +103 -0
  100. package/dist-esm/canvas/comment-mutations.mjs.map +7 -0
  101. package/dist-esm/canvas/comment-reactions.mjs +152 -0
  102. package/dist-esm/canvas/comment-reactions.mjs.map +7 -0
  103. package/dist-esm/canvas/comment-render.mjs +5 -3
  104. package/dist-esm/canvas/comment-render.mjs.map +2 -2
  105. package/dist-esm/canvas/comment-store.mjs +19 -9
  106. package/dist-esm/canvas/comment-store.mjs.map +2 -2
  107. package/dist-esm/canvas/comment-tool.mjs +67 -17
  108. package/dist-esm/canvas/comment-tool.mjs.map +2 -2
  109. package/dist-esm/canvas/comments-filter-menu.mjs +20 -38
  110. package/dist-esm/canvas/comments-filter-menu.mjs.map +2 -2
  111. package/dist-esm/canvas/comments-overflow-menu.mjs +23 -27
  112. package/dist-esm/canvas/comments-overflow-menu.mjs.map +2 -2
  113. package/dist-esm/canvas/comments-overlay.mjs +450 -479
  114. package/dist-esm/canvas/comments-overlay.mjs.map +3 -3
  115. package/dist-esm/canvas/comments-sidebar.mjs +38 -35
  116. package/dist-esm/canvas/comments-sidebar.mjs.map +2 -2
  117. package/dist-esm/canvas/comments-visibility-toggle.mjs +45 -0
  118. package/dist-esm/canvas/comments-visibility-toggle.mjs.map +7 -0
  119. package/dist-esm/canvas/context.mjs +1 -0
  120. package/dist-esm/canvas/context.mjs.map +7 -0
  121. package/dist-esm/canvas/hooks.mjs +28 -5
  122. package/dist-esm/canvas/hooks.mjs.map +2 -2
  123. package/dist-esm/canvas/options.mjs +21 -3
  124. package/dist-esm/canvas/options.mjs.map +2 -2
  125. package/dist-esm/canvas/pin-stacking.mjs +36 -0
  126. package/dist-esm/canvas/pin-stacking.mjs.map +7 -0
  127. package/dist-esm/canvas/sidebar-filters.mjs +2 -2
  128. package/dist-esm/canvas/sidebar-filters.mjs.map +2 -2
  129. package/dist-esm/canvas/state.mjs +29 -19
  130. package/dist-esm/canvas/state.mjs.map +2 -2
  131. package/dist-esm/canvas/thread-preview.mjs +170 -0
  132. package/dist-esm/canvas/thread-preview.mjs.map +7 -0
  133. package/dist-esm/canvas/thread-stack.mjs +190 -0
  134. package/dist-esm/canvas/thread-stack.mjs.map +7 -0
  135. package/dist-esm/canvas/thread-state.mjs +71 -22
  136. package/dist-esm/canvas/thread-state.mjs.map +2 -2
  137. package/dist-esm/canvas/thread-view.mjs +402 -0
  138. package/dist-esm/canvas/thread-view.mjs.map +7 -0
  139. package/dist-esm/clustering/computeClusterTable.mjs +4 -4
  140. package/dist-esm/clustering/computeClusterTable.mjs.map +2 -2
  141. package/dist-esm/clustering/runtime.mjs +119 -7
  142. package/dist-esm/clustering/runtime.mjs.map +2 -2
  143. package/dist-esm/index.d.mts +763 -333
  144. package/dist-esm/index.mjs +87 -41
  145. package/dist-esm/index.mjs.map +2 -2
  146. package/dist-esm/ui/byline.mjs +6 -4
  147. package/dist-esm/ui/byline.mjs.map +2 -2
  148. package/dist-esm/ui/comment-card.mjs +15 -8
  149. package/dist-esm/ui/comment-card.mjs.map +2 -2
  150. package/dist-esm/ui/comment-composer.mjs +148 -36
  151. package/dist-esm/ui/comment-composer.mjs.map +2 -2
  152. package/dist-esm/ui/comment-pin.mjs +24 -2
  153. package/dist-esm/ui/comment-pin.mjs.map +2 -2
  154. package/dist-esm/ui/comment-thread.mjs +9 -7
  155. package/dist-esm/ui/comment-thread.mjs.map +2 -2
  156. package/dist-esm/ui/comments-list.mjs +43 -29
  157. package/dist-esm/ui/comments-list.mjs.map +2 -2
  158. package/dist-esm/ui/count-badge.mjs +3 -2
  159. package/dist-esm/ui/count-badge.mjs.map +2 -2
  160. package/dist-esm/ui/emoji-picker.mjs +34 -0
  161. package/dist-esm/ui/emoji-picker.mjs.map +7 -0
  162. package/dist-esm/ui/empty-state.mjs +3 -3
  163. package/dist-esm/ui/empty-state.mjs.map +2 -2
  164. package/dist-esm/ui/format-time.mjs +4 -1
  165. package/dist-esm/ui/format-time.mjs.map +2 -2
  166. package/dist-esm/ui/reaction-picker.mjs +60 -0
  167. package/dist-esm/ui/reaction-picker.mjs.map +7 -0
  168. package/dist-esm/ui/reaction.mjs +60 -7
  169. package/dist-esm/ui/reaction.mjs.map +2 -2
  170. package/dist-esm/ui/reactions.mjs +21 -8
  171. package/dist-esm/ui/reactions.mjs.map +2 -2
  172. package/dist-esm/ui/reply-count.mjs +9 -0
  173. package/dist-esm/ui/reply-count.mjs.map +7 -0
  174. package/dist-esm/ui/send-button.mjs +20 -1
  175. package/dist-esm/ui/send-button.mjs.map +2 -2
  176. package/dist-esm/ui/tooltip-button.mjs +12 -0
  177. package/dist-esm/ui/tooltip-button.mjs.map +7 -0
  178. package/package.json +6 -6
  179. package/src/canvas/anchor-lifecycle.test.ts +290 -0
  180. package/src/canvas/anchor-lifecycle.ts +205 -0
  181. package/src/canvas/canvas.css +461 -46
  182. package/src/canvas/cluster-input.test.ts +31 -23
  183. package/src/canvas/cluster-input.ts +3 -4
  184. package/src/canvas/comment-body.tsx +1 -1
  185. package/src/canvas/comment-drafts.test.ts +55 -0
  186. package/src/canvas/comment-drafts.ts +46 -0
  187. package/src/canvas/comment-mutations.test.ts +410 -0
  188. package/src/canvas/comment-mutations.ts +288 -0
  189. package/src/canvas/comment-reactions.test.ts +132 -0
  190. package/src/canvas/comment-reactions.tsx +245 -0
  191. package/src/canvas/comment-render.test.ts +1 -1
  192. package/src/canvas/comment-render.ts +9 -5
  193. package/src/canvas/comment-store.ts +67 -27
  194. package/src/canvas/comment-tool.test.ts +162 -0
  195. package/src/canvas/comment-tool.tsx +105 -23
  196. package/src/canvas/comments-filter-menu.tsx +23 -32
  197. package/src/canvas/comments-overflow-menu.tsx +14 -21
  198. package/src/canvas/comments-overlay.tsx +695 -622
  199. package/src/canvas/comments-sidebar.test.ts +45 -0
  200. package/src/canvas/comments-sidebar.tsx +76 -58
  201. package/src/canvas/comments-visibility-toggle.tsx +49 -0
  202. package/src/canvas/context.ts +47 -0
  203. package/src/canvas/hooks.ts +52 -11
  204. package/src/canvas/options.test.ts +52 -2
  205. package/src/canvas/options.ts +133 -9
  206. package/src/canvas/pin-stacking.test.ts +114 -0
  207. package/src/canvas/pin-stacking.ts +57 -0
  208. package/src/canvas/sidebar-filters.ts +4 -3
  209. package/src/canvas/state.ts +92 -35
  210. package/src/canvas/thread-preview.test.ts +91 -0
  211. package/src/canvas/thread-preview.tsx +304 -0
  212. package/src/canvas/thread-stack.tsx +252 -0
  213. package/src/canvas/thread-state.test.ts +209 -0
  214. package/src/canvas/thread-state.ts +137 -36
  215. package/src/canvas/thread-view.tsx +516 -0
  216. package/src/clustering/computeClusterTable.test.ts +24 -23
  217. package/src/clustering/computeClusterTable.ts +5 -5
  218. package/src/clustering/runtime.test.ts +217 -7
  219. package/src/clustering/runtime.ts +171 -17
  220. package/src/clustering/types.ts +9 -9
  221. package/src/index.ts +76 -40
  222. package/src/ui/byline.tsx +7 -5
  223. package/src/ui/comment-card.tsx +20 -10
  224. package/src/ui/comment-composer.tsx +206 -40
  225. package/src/ui/comment-pin.tsx +26 -2
  226. package/src/ui/comment-thread.tsx +13 -6
  227. package/src/ui/comments-list.test.ts +31 -0
  228. package/src/ui/comments-list.tsx +45 -21
  229. package/src/ui/comments.css +566 -281
  230. package/src/ui/count-badge.tsx +9 -2
  231. package/src/ui/emoji-picker.test.ts +25 -0
  232. package/src/ui/emoji-picker.tsx +76 -0
  233. package/src/ui/empty-state.tsx +3 -3
  234. package/src/ui/format-time.ts +7 -2
  235. package/src/ui/reaction-picker.tsx +109 -0
  236. package/src/ui/reaction.tsx +121 -6
  237. package/src/ui/reactions.tsx +69 -10
  238. package/src/ui/reply-count.ts +16 -0
  239. package/src/ui/send-button.tsx +18 -3
  240. package/src/ui/tooltip-button.tsx +20 -0
  241. package/dist-cjs/canvas/region-options.js +0 -49
  242. package/dist-cjs/canvas/region-options.js.map +0 -7
  243. package/dist-cjs/ui/avatar.js +0 -43
  244. package/dist-cjs/ui/avatar.js.map +0 -7
  245. package/dist-cjs/ui/comment-mention.js +0 -42
  246. package/dist-cjs/ui/comment-mention.js.map +0 -7
  247. package/dist-cjs/ui/comment-text.js.map +0 -7
  248. package/dist-cjs/ui/mention-list.js +0 -70
  249. package/dist-cjs/ui/mention-list.js.map +0 -7
  250. package/dist-cjs/ui/mention-suggestion.js +0 -206
  251. package/dist-cjs/ui/mention-suggestion.js.map +0 -7
  252. package/dist-cjs/ui/mention.js.map +0 -7
  253. package/dist-cjs/ui/render-markdown.js +0 -63
  254. package/dist-cjs/ui/render-markdown.js.map +0 -7
  255. package/dist-esm/canvas/region-options.mjs +0 -29
  256. package/dist-esm/canvas/region-options.mjs.map +0 -7
  257. package/dist-esm/ui/avatar.mjs +0 -23
  258. package/dist-esm/ui/avatar.mjs.map +0 -7
  259. package/dist-esm/ui/comment-mention.mjs +0 -22
  260. package/dist-esm/ui/comment-mention.mjs.map +0 -7
  261. package/dist-esm/ui/comment-text.mjs +0 -9
  262. package/dist-esm/ui/comment-text.mjs.map +0 -7
  263. package/dist-esm/ui/mention-list.mjs +0 -50
  264. package/dist-esm/ui/mention-list.mjs.map +0 -7
  265. package/dist-esm/ui/mention-suggestion.mjs +0 -186
  266. package/dist-esm/ui/mention-suggestion.mjs.map +0 -7
  267. package/dist-esm/ui/mention.mjs +0 -11
  268. package/dist-esm/ui/mention.mjs.map +0 -7
  269. package/dist-esm/ui/render-markdown.mjs +0 -45
  270. package/dist-esm/ui/render-markdown.mjs.map +0 -7
  271. package/src/canvas/region-options.ts +0 -57
  272. package/src/ui/avatar.tsx +0 -31
  273. package/src/ui/comment-mention.ts +0 -47
  274. package/src/ui/comment-text.tsx +0 -12
  275. package/src/ui/mention-list.tsx +0 -106
  276. package/src/ui/mention-suggestion.test.ts +0 -18
  277. package/src/ui/mention-suggestion.tsx +0 -285
  278. package/src/ui/mention.tsx +0 -9
  279. package/src/ui/render-markdown.tsx +0 -72
@@ -1,9 +1,16 @@
1
1
  /** @public */
2
2
  export interface CountBadgeProps {
3
3
  count: number
4
+ /** The badge's list is showing — shows the active/selected indicator state, as an open pin does. */
5
+ open?: boolean
4
6
  }
5
7
 
6
8
  /** @public @react */
7
- export function CountBadge({ count }: CountBadgeProps) {
8
- return <div className="cmt-count-badge">{count}</div>
9
+ export function CountBadge({ count, open }: CountBadgeProps) {
10
+ // `tlui-cmt-marker` carries the resting shadow, the hover lift, and the open ring — the same
11
+ // treatment a comment pin wears, so the two markers behave alike.
12
+ const className = ['tlui-cmt-marker', 'tlui-cmt-count-badge', open && 'tlui-cmt-marker--open']
13
+ .filter(Boolean)
14
+ .join(' ')
15
+ return <div className={className}>{count}</div>
9
16
  }
@@ -0,0 +1,25 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { DEFAULT_REACTION_EMOJI, isAllowedReactionEmoji } from './emoji-picker'
3
+
4
+ describe('isAllowedReactionEmoji', () => {
5
+ it('accepts every emoji in the default palette', () => {
6
+ for (const emoji of DEFAULT_REACTION_EMOJI) {
7
+ expect(isAllowedReactionEmoji(emoji)).toBe(true)
8
+ }
9
+ })
10
+
11
+ it('rejects emoji not in the palette', () => {
12
+ expect(isAllowedReactionEmoji('🦄')).toBe(false)
13
+ })
14
+
15
+ // the record's emoji field is a free string, so this is the guard against junk values
16
+ it('rejects arbitrary non-emoji strings', () => {
17
+ expect(isAllowedReactionEmoji('not an emoji')).toBe(false)
18
+ expect(isAllowedReactionEmoji('')).toBe(false)
19
+ })
20
+
21
+ it('checks against a custom palette when given one', () => {
22
+ expect(isAllowedReactionEmoji('🦄', ['🦄', '🌈'])).toBe(true)
23
+ expect(isAllowedReactionEmoji('👍', ['🦄', '🌈'])).toBe(false)
24
+ })
25
+ })
@@ -0,0 +1,76 @@
1
+ import { RenderReaction, defaultRenderReaction } from './reaction'
2
+
3
+ /**
4
+ * The reaction emoji palette: the small, opinionated set the picker offers *and* the set a
5
+ * reaction is allowed to use. It's deliberately short — a quick affordance under a comment, not a
6
+ * full emoji keyboard — and it's the single source of truth for "which emoji may a reaction
7
+ * carry". A reaction's `emoji` field is a free-form `string`, so this palette is what keeps it to
8
+ * real, offered emoji; check membership with {@link isAllowedReactionEmoji}. Pass a custom `emoji`
9
+ * array to `ReactionPicker` to offer a different set — validate against that same set if you do.
10
+ *
11
+ * @public
12
+ */
13
+ export const DEFAULT_REACTION_EMOJI = ['👍', '👎', '❤️', '🎉', '😄', '😮', '😢', '🙏', '👀', '🔥']
14
+
15
+ /**
16
+ * Whether `emoji` belongs to a reaction palette (defaults to {@link DEFAULT_REACTION_EMOJI}). Use
17
+ * it to reject arbitrary strings before writing a reaction, so a scripted client can't spam a
18
+ * comment with junk `emoji` values that the picker would never offer. Pass a custom `palette` to
19
+ * match a customized picker.
20
+ *
21
+ * @public
22
+ */
23
+ export function isAllowedReactionEmoji(
24
+ emoji: string,
25
+ palette: readonly string[] = DEFAULT_REACTION_EMOJI
26
+ ): boolean {
27
+ return palette.includes(emoji)
28
+ }
29
+
30
+ /** @public */
31
+ export interface EmojiPickerProps {
32
+ /** The emoji to offer. Defaults to `DEFAULT_REACTION_EMOJI`. */
33
+ emoji?: string[]
34
+ /** Emoji the current user has already reacted with; shown as pressed. */
35
+ selected?: string[]
36
+ /** Called when an emoji is chosen. */
37
+ onSelect?(emoji: string): void
38
+ /** How to draw each emoji token. Defaults to the token string (OS emoji font). */
39
+ renderReaction?: RenderReaction
40
+ }
41
+
42
+ /**
43
+ * A grid of emoji to react with. Presentational and unpositioned — the host owns placement and
44
+ * dismissal; `ReactionPicker` renders it in a dropdown under the add-reaction button.
45
+ * @public @react
46
+ */
47
+ export function EmojiPicker({
48
+ emoji = DEFAULT_REACTION_EMOJI,
49
+ selected,
50
+ onSelect,
51
+ renderReaction = defaultRenderReaction,
52
+ }: EmojiPickerProps) {
53
+ return (
54
+ <div className="tlui-cmt-emoji-picker" role="group">
55
+ {emoji.map((value) => {
56
+ const active = selected?.includes(value) ?? false
57
+ return (
58
+ <button
59
+ key={value}
60
+ type="button"
61
+ className={
62
+ active
63
+ ? 'tlui-cmt-emoji-picker__item tlui-cmt-emoji-picker__item--active'
64
+ : 'tlui-cmt-emoji-picker__item'
65
+ }
66
+ aria-label={value}
67
+ aria-pressed={active}
68
+ onClick={() => onSelect?.(value)}
69
+ >
70
+ {renderReaction(value)}
71
+ </button>
72
+ )
73
+ })}
74
+ </div>
75
+ )
76
+ }
@@ -6,11 +6,11 @@ export interface EmptyStateProps {
6
6
  /** The empty state shown when a thread has no comments yet. @public @react */
7
7
  export function EmptyState({ message }: EmptyStateProps) {
8
8
  return (
9
- <div className="cmt-empty">
10
- <div className="cmt-empty__icon" aria-hidden="true">
9
+ <div className="tlui-cmt-empty">
10
+ <div className="tlui-cmt-empty__icon" aria-hidden="true">
11
11
  💬
12
12
  </div>
13
- <p className="cmt-empty__message">{message}</p>
13
+ <p className="tlui-cmt-empty__message">{message}</p>
14
14
  </div>
15
15
  )
16
16
  }
@@ -9,7 +9,7 @@ const DIVISIONS = [
9
9
  ] as const
10
10
 
11
11
  /**
12
- * Format an ISO datetime as relative time ("2 hours ago", "yesterday", "last week").
12
+ * Format an ISO datetime as short relative time ("2 hr. ago", "yesterday", "last wk.").
13
13
  * Locale-aware via Intl.RelativeTimeFormat.
14
14
  * @public
15
15
  */
@@ -17,8 +17,13 @@ export function formatRelativeTime(iso: string, locale = 'en'): string {
17
17
  const then = new Date(iso).getTime()
18
18
  if (Number.isNaN(then)) return ''
19
19
 
20
- const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' })
20
+ const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto', style: 'short' })
21
21
  let duration = (then - Date.now()) / 1000
22
+ // Under a minute is just "now" — with second granularity the label visibly ticks while
23
+ // typing a reply (every keystroke re-renders the card).
24
+ if (Math.abs(duration) < 60) {
25
+ return rtf.format(0, 'second')
26
+ }
22
27
  for (const division of DIVISIONS) {
23
28
  if (Math.abs(duration) < division.amount) {
24
29
  return rtf.format(Math.round(duration), division.unit)
@@ -0,0 +1,109 @@
1
+ import { useCallback, useId, type ComponentType } from 'react'
2
+ import {
3
+ TldrawUiDropdownMenuContent,
4
+ TldrawUiDropdownMenuRoot,
5
+ TldrawUiDropdownMenuTrigger,
6
+ tlmenus,
7
+ useMaybeEditor,
8
+ useTranslation,
9
+ } from 'tldraw'
10
+ import { EmojiPicker, type EmojiPickerProps } from './emoji-picker'
11
+ import { RenderReaction } from './reaction'
12
+ import { TooltipButton } from './tooltip-button'
13
+
14
+ /** @public */
15
+ export interface ReactionPickerProps {
16
+ /** The emoji to offer. Defaults to `DEFAULT_REACTION_EMOJI`. */
17
+ emoji?: string[]
18
+ /** Emoji the current user has already reacted with; shown as pressed in the grid. */
19
+ selected?: string[]
20
+ /** Called when an emoji is chosen. */
21
+ onSelect?(emoji: string): void
22
+ /** How to draw each emoji token. Defaults to the token string (OS emoji font). */
23
+ renderReaction?: RenderReaction
24
+ /**
25
+ * What the button opens — the thing that produces a token. Defaults to `EmojiPicker`, the grid of
26
+ * emoji. Swap it for any component taking the same props to offer something else entirely (the
27
+ * drawn reactions example replaces it with a canvas you draw in); the props are passed straight
28
+ * through either way.
29
+ */
30
+ palette?: ComponentType<EmojiPickerProps>
31
+ /**
32
+ * Identifies the menu in tldraw's global menu registry, which keys open/closed state by id.
33
+ * A thread renders one picker per comment, so this must differ per comment — sharing an id
34
+ * makes every picker in the thread open at once. Defaults to a per-instance generated id;
35
+ * pass one only for a stabler, more debuggable value.
36
+ */
37
+ menuId?: string
38
+ /** Class for the trigger button. Defaults to the card-action style, matching the ⋯ button. */
39
+ className?: string
40
+ }
41
+
42
+ /**
43
+ * The add-reaction affordance: a smiley button that opens an emoji grid.
44
+ *
45
+ * Anchored to its own button rather than to the reactions row, so the menu keeps its position as
46
+ * reactions are added and the row reflows.
47
+ *
48
+ * Picking an emoji dismisses the grid — adding and removing alike, since either way the picker has
49
+ * done its job — and hands focus back to the trigger.
50
+ * @public @react
51
+ */
52
+ export function ReactionPicker({
53
+ emoji,
54
+ selected,
55
+ onSelect,
56
+ renderReaction,
57
+ palette: Palette = EmojiPicker,
58
+ menuId,
59
+ className = 'tlui-cmt-thread__action',
60
+ }: ReactionPickerProps) {
61
+ const msg = useTranslation()
62
+ const editor = useMaybeEditor()
63
+ // Unique per mounted picker unless the host supplies something stabler — see `menuId`.
64
+ const generatedId = useId()
65
+ const id = menuId ?? `comment-reactions-${generatedId}`
66
+
67
+ // The dropdown's open state lives in tldraw's global menu registry, so dropping the entry is
68
+ // what closes it. The palette's tokens are plain buttons — a swappable component, not radix
69
+ // menu items — so nothing dismisses the menu on its own.
70
+ const handleSelect = useCallback(
71
+ (value: string) => {
72
+ onSelect?.(value)
73
+ tlmenus.deleteOpenMenu(id, editor?.contextId)
74
+ },
75
+ [onSelect, id, editor]
76
+ )
77
+
78
+ return (
79
+ <TldrawUiDropdownMenuRoot id={id}>
80
+ <TldrawUiDropdownMenuTrigger>
81
+ <TooltipButton tooltip={msg('comments.add-reaction')} className={className}>
82
+ <SmileyIcon />
83
+ </TooltipButton>
84
+ </TldrawUiDropdownMenuTrigger>
85
+ {/* left-aligned under the trigger, so the grid opens rightward off the card's edge */}
86
+ <TldrawUiDropdownMenuContent side="bottom" align="start" alignOffset={0} sideOffset={4}>
87
+ <Palette
88
+ emoji={emoji}
89
+ selected={selected}
90
+ onSelect={handleSelect}
91
+ renderReaction={renderReaction}
92
+ />
93
+ </TldrawUiDropdownMenuContent>
94
+ </TldrawUiDropdownMenuRoot>
95
+ )
96
+ }
97
+
98
+ function SmileyIcon() {
99
+ return (
100
+ <svg width="15" height="15" viewBox="0 0 15 15" fill="none" aria-hidden="true">
101
+ <path
102
+ d="M7.49991 0.876892C3.84222 0.876892 0.877075 3.84204 0.877075 7.49972C0.877075 11.1574 3.84222 14.1226 7.49991 14.1226C11.1576 14.1226 14.1227 11.1574 14.1227 7.49972C14.1227 3.84204 11.1576 0.876892 7.49991 0.876892ZM1.82708 7.49972C1.82708 4.36671 4.36689 1.82689 7.49991 1.82689C10.6329 1.82689 13.1727 4.36671 13.1727 7.49972C13.1727 10.6327 10.6329 13.1726 7.49991 13.1726C4.36689 13.1726 1.82708 10.6327 1.82708 7.49972ZM5.03747 9.21395C4.87949 8.98746 4.56782 8.93193 4.34133 9.08991C4.11484 9.24789 4.05931 9.55956 4.21729 9.78605C4.93926 10.8211 6.14033 11.5 7.50004 11.5C8.85974 11.5 10.0608 10.8211 10.7828 9.78605C10.9408 9.55956 10.8852 9.24789 10.6587 9.08991C10.4323 8.93193 10.1206 8.98746 9.9626 9.21395C9.41963 9.99238 8.51907 10.5 7.50004 10.5C6.481 10.5 5.58044 9.99238 5.03747 9.21395ZM5.37503 6.84998C5.85828 6.84998 6.25003 6.45815 6.25003 5.97498C6.25003 5.4918 5.85828 5.09998 5.37503 5.09998C4.89179 5.09998 4.50003 5.4918 4.50003 5.97498C4.50003 6.45815 4.89179 6.84998 5.37503 6.84998ZM10.5 5.97498C10.5 6.45815 10.1083 6.84998 9.62503 6.84998C9.14179 6.84998 8.75003 6.45815 8.75003 5.97498C8.75003 5.4918 9.14179 5.09998 9.62503 5.09998C10.1083 5.09998 10.5 5.4918 10.5 5.97498Z"
103
+ fill="currentColor"
104
+ fillRule="evenodd"
105
+ clipRule="evenodd"
106
+ />
107
+ </svg>
108
+ )
109
+ }
@@ -1,16 +1,131 @@
1
+ import { ComponentType, ReactNode } from 'react'
2
+ import { TldrawUiTooltip, useTranslation } from 'tldraw'
3
+ import { ReactionReactor } from './reactions'
4
+
5
+ /** Render a reaction token to its visual — the emoji glyph by default. @public */
6
+ export type RenderReaction = (token: string) => ReactNode
7
+
8
+ /** The default reaction renderer: emits the token string for the OS emoji font to draw. @public */
9
+ export function defaultRenderReaction(token: string): ReactNode {
10
+ return token
11
+ }
12
+
1
13
  /** @public */
2
14
  export interface ReactionProps {
3
15
  emoji: string
4
16
  count: number
5
17
  active: boolean
18
+ /** How to draw the emoji token. Defaults to the token string (OS emoji font). */
19
+ renderReaction?: RenderReaction
20
+ /** Who reacted with this emoji, in reaction order — shown when the pill is hovered. */
21
+ reactors: ReactionReactor[]
22
+ /** Whether hovering shows the reactor list. Pass false to suppress it — e.g. while another
23
+ * popup menu on the comment is open. Defaults to true. */
24
+ enableHoverList?: boolean
25
+ /** The hover affordance for the pill, naming who reacted. It receives the reactors and the pill
26
+ * itself (as `children`) and returns the whole thing — so it owns the tooltip, its box, size,
27
+ * shape, and position, not just the text inside. Defaults to `DefaultReactionTooltip`, which
28
+ * hangs the built-in inline sentence below the pill. */
29
+ ReactionTooltip?: ComponentType<ReactionTooltipProps>
30
+ /** Called when the pill is pressed — toggles the current user's reaction. */
31
+ onClick?(): void
6
32
  }
7
33
 
8
- /** A single emoji reaction pill with a count, highlighted when the user reacted. @public @react */
9
- export function Reaction({ emoji, count, active }: ReactionProps) {
10
- return (
11
- <button className={active ? 'cmt-reaction cmt-reaction--active' : 'cmt-reaction'} type="button">
12
- <span className="cmt-reaction__emoji">{emoji}</span>
13
- <span className="cmt-reaction__count">{count}</span>
34
+ /**
35
+ * A single emoji reaction pill with a count, highlighted when the user reacted. Hovering it shows a
36
+ * tooltip naming who reacted (the current user included), when `enableHoverList` is set.
37
+ * @public @react
38
+ */
39
+ export function Reaction({
40
+ emoji,
41
+ count,
42
+ active,
43
+ reactors,
44
+ enableHoverList = true,
45
+ renderReaction = defaultRenderReaction,
46
+ ReactionTooltip = DefaultReactionTooltip,
47
+ onClick,
48
+ }: ReactionProps) {
49
+ const pill = (
50
+ <button
51
+ className={active ? 'tlui-cmt-reaction tlui-cmt-reaction--active' : 'tlui-cmt-reaction'}
52
+ type="button"
53
+ // The emoji alone announces as its unicode name ("thumbs up"); pairing it with the count
54
+ // is what makes the pill's state legible to a screen reader.
55
+ aria-label={`${emoji} ${count}`}
56
+ aria-pressed={active}
57
+ onClick={onClick}
58
+ >
59
+ <span className="tlui-cmt-reaction__emoji">{renderReaction(emoji)}</span>
60
+ <span className="tlui-cmt-reaction__count">{count}</span>
14
61
  </button>
15
62
  )
63
+
64
+ // A bare pill when hovering is suppressed, or when there's no one to name.
65
+ if (!enableHoverList || reactors.length === 0) return pill
66
+
67
+ // Hand the whole hover affordance to the tooltip component: it wraps the pill and owns how the
68
+ // reactor list is presented.
69
+ return <ReactionTooltip reactors={reactors}>{pill}</ReactionTooltip>
70
+ }
71
+
72
+ /** @public */
73
+ export interface ReactionTooltipProps {
74
+ /** Who reacted with this emoji, in reaction order. */
75
+ reactors: ReactionReactor[]
76
+ /** The reaction pill to wrap. Anchor the hover affordance to this and render it. */
77
+ children: ReactNode
78
+ }
79
+
80
+ /**
81
+ * The default reactor tooltip: hangs the built-in inline sentence (`DefaultReactionTooltipContent`)
82
+ * in a standard tooltip below the pill — the same tooltip the toolbar uses. Replace it via
83
+ * `CommentingComponents.ReactionTooltip` to present the reactor list any other way (a different box,
84
+ * avatars, a banner, anywhere on screen); it receives the pill as `children`.
85
+ * @public @react
86
+ */
87
+ export function DefaultReactionTooltip({ reactors, children }: ReactionTooltipProps) {
88
+ return (
89
+ <TldrawUiTooltip side="bottom" content={<DefaultReactionTooltipContent reactors={reactors} />}>
90
+ {children}
91
+ </TldrawUiTooltip>
92
+ )
93
+ }
94
+
95
+ /**
96
+ * The default reactor sentence naming who reacted — up to three names spelled out, then "and N
97
+ * others" (e.g. "You reacted", "You and Bo reacted", "You, Bo and Ada reacted", "You, Bo, Ada and 2
98
+ * others reacted"). The wording lives in the `comments.reacted-*` translation strings so each locale
99
+ * controls the grammar. Exported so a custom `ReactionTooltip` can reuse it inside its own box.
100
+ * @public @react
101
+ */
102
+ export function DefaultReactionTooltipContent({ reactors }: { reactors: ReactionReactor[] }) {
103
+ const msg = useTranslation()
104
+ const names = reactors.map((reactor) =>
105
+ reactor.you ? msg('comments.mention-you') : reactor.name
106
+ )
107
+ const [a, b, c] = names
108
+ switch (names.length) {
109
+ case 0:
110
+ return null
111
+ case 1:
112
+ return <>{msg('comments.reacted-1').replace('{a}', a)}</>
113
+ case 2:
114
+ return <>{msg('comments.reacted-2').replace('{a}', a).replace('{b}', b)}</>
115
+ case 3:
116
+ return <>{msg('comments.reacted-3').replace('{a}', a).replace('{b}', b).replace('{c}', c)}</>
117
+ default: {
118
+ const others = names.length - 3
119
+ const template = others === 1 ? 'comments.reacted-more-one' : 'comments.reacted-more'
120
+ return (
121
+ <>
122
+ {msg(template)
123
+ .replace('{a}', a)
124
+ .replace('{b}', b)
125
+ .replace('{c}', c)
126
+ .replace('{count}', String(others))}
127
+ </>
128
+ )
129
+ }
130
+ }
16
131
  }
@@ -1,15 +1,74 @@
1
- import { Reaction } from './reaction'
1
+ import { ComponentType } from 'react'
2
+ import { Reaction, ReactionTooltipProps, RenderReaction } from './reaction'
2
3
 
3
- /** The row of reactions under a comment, plus an add-reaction button. @public @react */
4
- export function Reactions() {
4
+ /** One person who reacted with a given emoji, for the hover list. @public */
5
+ export interface ReactionReactor {
6
+ /** Display name of the person who reacted. */
7
+ name: string
8
+ /** True for the current user. */
9
+ you: boolean
10
+ }
11
+
12
+ /** One emoji's reactions on a comment, already tallied. @public */
13
+ export interface ReactionSummary {
14
+ emoji: string
15
+ /** How many people reacted with this emoji. */
16
+ count: number
17
+ /** True when the current user is one of them — renders the pill highlighted. */
18
+ active: boolean
19
+ /** Who reacted with this emoji, in reaction order — shown in the hover list. */
20
+ reactors: ReactionReactor[]
21
+ }
22
+
23
+ /** @public */
24
+ export interface ReactionsProps {
25
+ /** The comment's reactions, grouped by emoji. Empty renders nothing. */
26
+ reactions: ReactionSummary[]
27
+ /** Toggles the current user's reaction for an emoji. */
28
+ onToggle?(emoji: string): void
29
+ /** Whether the current user may react. False makes the pills inert (e.g. a signed-out
30
+ * viewer, or a read-only thread) while still showing counts. */
31
+ canReact?: boolean
32
+ /** Whether hovering a pill shows its reactor list. Pass false to suppress it — e.g. while
33
+ * another popup menu on the comment is open. Defaults to true. */
34
+ enableHoverList?: boolean
35
+ /** How to draw each emoji token. Defaults to the token string (OS emoji font). */
36
+ renderReaction?: RenderReaction
37
+ /** The tooltip naming who reacted, shown when a pill is hovered. Defaults to an inline sentence
38
+ * (`DefaultReactionTooltip`). Swap it to render the reactor list however you like. */
39
+ ReactionTooltip?: ComponentType<ReactionTooltipProps>
40
+ }
41
+
42
+ /**
43
+ * The row of tallied reactions under a comment. Presentational — the host supplies the summaries
44
+ * and owns what toggling does. Hovering a pill lists who reacted (see `Reaction`).
45
+ *
46
+ * The add-reaction affordance is a separate component (`ReactionPicker`) so it can live outside
47
+ * this row — on a comment card it sits with the card's hover actions, which keeps its position
48
+ * fixed as reactions are added here.
49
+ * @public @react
50
+ */
51
+ export function Reactions({
52
+ reactions,
53
+ onToggle,
54
+ canReact = true,
55
+ enableHoverList = true,
56
+ renderReaction,
57
+ ReactionTooltip,
58
+ }: ReactionsProps) {
59
+ if (reactions.length === 0) return null
5
60
  return (
6
- <div className="cmt-reactions">
7
- <Reaction emoji="👍" count={3} active={true} />
8
- <Reaction emoji="🎉" count={1} active={false} />
9
- <Reaction emoji="👀" count={2} active={false} />
10
- <button className="cmt-reaction cmt-reaction--add" type="button" aria-label="Add reaction">
11
-
12
- </button>
61
+ <div className="tlui-cmt-reactions">
62
+ {reactions.map((reaction) => (
63
+ <Reaction
64
+ key={reaction.emoji}
65
+ {...reaction}
66
+ enableHoverList={enableHoverList}
67
+ renderReaction={renderReaction}
68
+ ReactionTooltip={ReactionTooltip}
69
+ onClick={canReact ? () => onToggle?.(reaction.emoji) : undefined}
70
+ />
71
+ ))}
13
72
  </div>
14
73
  )
15
74
  }
@@ -0,0 +1,16 @@
1
+ import { useTranslation } from 'tldraw'
2
+
3
+ /**
4
+ * The localized "N replies" label (e.g. "1 reply", "3 replies") for a thread with `replyCount`
5
+ * replies — the comments after the opening one. Returns null when there are none, so callers render
6
+ * nothing rather than "0 replies". Shared by the sidebar row and the hover preview so both read the
7
+ * same, with each site owning where it puts the text.
8
+ */
9
+ export function replyCountLabel(
10
+ msg: ReturnType<typeof useTranslation>,
11
+ replyCount: number
12
+ ): string | null {
13
+ if (replyCount <= 0) return null
14
+ const key = replyCount === 1 ? 'comments.replies-one' : 'comments.replies'
15
+ return msg(key).replace('{count}', String(replyCount))
16
+ }
@@ -1,15 +1,30 @@
1
1
  /** @public */
2
2
  export interface SendButtonProps {
3
+ /** The button's accessible name (e.g. "Send"). Shown as an up-arrow icon, so this is its label. */
3
4
  label: string
4
5
  disabled?: boolean
5
6
  onClick?(): void
6
7
  }
7
8
 
8
- /** The button that posts a comment. @public @react */
9
+ /** The button that posts a comment — an up arrow. @public @react */
9
10
  export function SendButton({ label, disabled, onClick }: SendButtonProps) {
10
11
  return (
11
- <button className="cmt-send" type="button" disabled={disabled} onClick={onClick}>
12
- {label}
12
+ <button
13
+ className="tlui-cmt-send"
14
+ type="button"
15
+ disabled={disabled}
16
+ onClick={onClick}
17
+ aria-label={label}
18
+ title={label}
19
+ >
20
+ <svg width="15" height="15" viewBox="0 0 15 15" fill="none" aria-hidden="true">
21
+ <path
22
+ d="M7.14645 2.14645C7.34171 1.95118 7.65829 1.95118 7.85355 2.14645L11.8536 6.14645C12.0488 6.34171 12.0488 6.65829 11.8536 6.85355C11.6583 7.04882 11.3417 7.04882 11.1464 6.85355L8 3.70711L8 12.5C8 12.7761 7.77614 13 7.5 13C7.22386 13 7 12.7761 7 12.5L7 3.70711L3.85355 6.85355C3.65829 7.04882 3.34171 7.04882 3.14645 6.85355C2.95118 6.65829 2.95118 6.34171 3.14645 6.14645L7.14645 2.14645Z"
23
+ fill="currentColor"
24
+ fillRule="evenodd"
25
+ clipRule="evenodd"
26
+ />
27
+ </svg>
13
28
  </button>
14
29
  )
15
30
  }
@@ -0,0 +1,20 @@
1
+ import { type ButtonHTMLAttributes, forwardRef } from 'react'
2
+ import { TldrawUiTooltip } from 'tldraw'
3
+
4
+ interface TooltipButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
5
+ tooltip: string
6
+ }
7
+
8
+ /** An icon button labelled by a styled tooltip. Forwards ref and props to the underlying button
9
+ * so `asChild` triggers (dropdown, tooltip) both compose onto the real element. */
10
+ export const TooltipButton = forwardRef<HTMLButtonElement, TooltipButtonProps>(
11
+ function TooltipButton({ tooltip, children, ...props }, ref) {
12
+ return (
13
+ <TldrawUiTooltip content={tooltip}>
14
+ <button ref={ref} type="button" aria-label={tooltip} {...props}>
15
+ {children}
16
+ </button>
17
+ </TldrawUiTooltip>
18
+ )
19
+ }
20
+ )
@@ -1,49 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
- var region_options_exports = {};
20
- __export(region_options_exports, {
21
- DEFAULT_REGION_COMMENT_OPTIONS: () => DEFAULT_REGION_COMMENT_OPTIONS,
22
- getRegionCommentOptions: () => getRegionCommentOptions,
23
- setRegionCommentOptions: () => setRegionCommentOptions
24
- });
25
- module.exports = __toCommonJS(region_options_exports);
26
- var import_tldraw = require("tldraw");
27
- const DEFAULT_REGION_COMMENT_OPTIONS = {
28
- enabled: false,
29
- pinCorner: { x: 1, y: 1 },
30
- reveal: "pointer",
31
- move: "pin",
32
- resize: "corners"
33
- };
34
- const byEditor = /* @__PURE__ */ new WeakMap();
35
- function getEditorAtom(editor) {
36
- let a = byEditor.get(editor);
37
- if (!a) {
38
- a = (0, import_tldraw.atom)("regionCommentOptions", DEFAULT_REGION_COMMENT_OPTIONS);
39
- byEditor.set(editor, a);
40
- }
41
- return a;
42
- }
43
- function setRegionCommentOptions(editor, options) {
44
- getEditorAtom(editor).set(options);
45
- }
46
- function getRegionCommentOptions(editor) {
47
- return getEditorAtom(editor).get();
48
- }
49
- //# sourceMappingURL=region-options.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/canvas/region-options.ts"],
4
- "sourcesContent": ["import { atom, Atom, Editor, VecLike } from 'tldraw'\n\n/**\n * The configurable dimensions of region comments \u2014 a design surface for prototyping the interaction\n * before settling it. Region is **off by default**, so a consumer that leaves it unset keeps plain\n * click-only point/shape commenting. Set it per editor through `<CanvasComments regionOptions>`.\n * @public\n */\nexport interface RegionCommentOptions {\n\t/** Whether dragging the comment tool out creates a region anchor. Off \u2192 click-only. */\n\tenabled: boolean\n\t/** Which corner the pin and composer sit on, as a normalized 0\u20131 offset. Default bottom-right. */\n\tpinCorner: VecLike\n\t/** When the dashed box and its handles reveal: while the pointer is within the region, while the\n\t * pin is hovered, or only while the thread is open. */\n\treveal: 'pointer' | 'pin-hover' | 'open'\n\t/** How a region is moved: dragging its pin, dragging its body, or either. */\n\tmove: 'pin' | 'body' | 'both'\n\t/** The resize affordance: corner handles, edge handles, or none. */\n\tresize: 'corners' | 'edges' | 'none'\n}\n\n/** The out-of-the-box region config: disabled, and \u2014 when a consumer enables it \u2014 the current\n * bottom-right / pointer-reveal / pin-move / corner-resize behaviour.\n * @public */\nexport const DEFAULT_REGION_COMMENT_OPTIONS: RegionCommentOptions = {\n\tenabled: false,\n\tpinCorner: { x: 1, y: 1 },\n\treveal: 'pointer',\n\tmove: 'pin',\n\tresize: 'corners',\n}\n\n// Per-editor region config. The overlay publishes its merged `regionOptions` here so the comment\n// tool \u2014 which has no props of its own \u2014 can read the same per-instance config at interaction time.\n// Held in an atom so reactive readers (e.g. `anchorPagePoint`, called inside `useValue`) recompute\n// when the config changes; a plain map read wouldn't, leaving the pin at the stale default corner.\nconst byEditor = new WeakMap<Editor, Atom<RegionCommentOptions>>()\n\nfunction getEditorAtom(editor: Editor): Atom<RegionCommentOptions> {\n\tlet a = byEditor.get(editor)\n\tif (!a) {\n\t\ta = atom('regionCommentOptions', DEFAULT_REGION_COMMENT_OPTIONS)\n\t\tbyEditor.set(editor, a)\n\t}\n\treturn a\n}\n\n/** Publish an editor's region config (called by the overlay from its `regionOptions` prop). */\nexport function setRegionCommentOptions(editor: Editor, options: RegionCommentOptions): void {\n\tgetEditorAtom(editor).set(options)\n}\n\n/** The editor's region config, or the disabled default when none was set. */\nexport function getRegionCommentOptions(editor: Editor): RegionCommentOptions {\n\treturn getEditorAtom(editor).get()\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAA4C;AAyBrC,MAAM,iCAAuD;AAAA,EACnE,SAAS;AAAA,EACT,WAAW,EAAE,GAAG,GAAG,GAAG,EAAE;AAAA,EACxB,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,QAAQ;AACT;AAMA,MAAM,WAAW,oBAAI,QAA4C;AAEjE,SAAS,cAAc,QAA4C;AAClE,MAAI,IAAI,SAAS,IAAI,MAAM;AAC3B,MAAI,CAAC,GAAG;AACP,YAAI,oBAAK,wBAAwB,8BAA8B;AAC/D,aAAS,IAAI,QAAQ,CAAC;AAAA,EACvB;AACA,SAAO;AACR;AAGO,SAAS,wBAAwB,QAAgB,SAAqC;AAC5F,gBAAc,MAAM,EAAE,IAAI,OAAO;AAClC;AAGO,SAAS,wBAAwB,QAAsC;AAC7E,SAAO,cAAc,MAAM,EAAE,IAAI;AAClC;",
6
- "names": []
7
- }