@liveblocks/react-ui 2.7.0-beta2 → 2.7.0-versions

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 (142) hide show
  1. package/_private/README.md +5 -0
  2. package/_private/package.json +4 -0
  3. package/dist/_private/index.d.mts +72 -0
  4. package/dist/_private/index.d.ts +72 -0
  5. package/dist/_private/index.js +46 -0
  6. package/dist/_private/index.js.map +1 -0
  7. package/dist/_private/index.mjs +21 -0
  8. package/dist/_private/index.mjs.map +1 -0
  9. package/dist/components/Comment.js +7 -65
  10. package/dist/components/Comment.js.map +1 -1
  11. package/dist/components/Comment.mjs +9 -66
  12. package/dist/components/Comment.mjs.map +1 -1
  13. package/dist/components/Composer.js +101 -217
  14. package/dist/components/Composer.js.map +1 -1
  15. package/dist/components/Composer.mjs +104 -220
  16. package/dist/components/Composer.mjs.map +1 -1
  17. package/dist/components/HistoryVersionSummary.js +42 -0
  18. package/dist/components/HistoryVersionSummary.js.map +1 -0
  19. package/dist/components/HistoryVersionSummary.mjs +40 -0
  20. package/dist/components/HistoryVersionSummary.mjs.map +1 -0
  21. package/dist/components/HistoryVersionSummaryList.js +22 -0
  22. package/dist/components/HistoryVersionSummaryList.js.map +1 -0
  23. package/dist/components/HistoryVersionSummaryList.mjs +20 -0
  24. package/dist/components/HistoryVersionSummaryList.mjs.map +1 -0
  25. package/dist/components/InboxNotification.js +10 -28
  26. package/dist/components/InboxNotification.js.map +1 -1
  27. package/dist/components/InboxNotification.mjs +10 -28
  28. package/dist/components/InboxNotification.mjs.map +1 -1
  29. package/dist/components/Thread.js +0 -2
  30. package/dist/components/Thread.js.map +1 -1
  31. package/dist/components/Thread.mjs +0 -2
  32. package/dist/components/Thread.mjs.map +1 -1
  33. package/dist/components/internal/Button.js +8 -1
  34. package/dist/components/internal/Button.js.map +1 -1
  35. package/dist/components/internal/Button.mjs +8 -1
  36. package/dist/components/internal/Button.mjs.map +1 -1
  37. package/dist/components/internal/EmojiPicker.js +3 -3
  38. package/dist/components/internal/EmojiPicker.js.map +1 -1
  39. package/dist/components/internal/EmojiPicker.mjs +3 -3
  40. package/dist/components/internal/EmojiPicker.mjs.map +1 -1
  41. package/dist/components/internal/InboxNotificationThread.js +2 -10
  42. package/dist/components/internal/InboxNotificationThread.js.map +1 -1
  43. package/dist/components/internal/InboxNotificationThread.mjs +3 -11
  44. package/dist/components/internal/InboxNotificationThread.mjs.map +1 -1
  45. package/dist/components/internal/User.js +3 -19
  46. package/dist/components/internal/User.js.map +1 -1
  47. package/dist/components/internal/User.mjs +3 -19
  48. package/dist/components/internal/User.mjs.map +1 -1
  49. package/dist/icons/ArrowUp.js +15 -0
  50. package/dist/icons/ArrowUp.js.map +1 -0
  51. package/dist/icons/ArrowUp.mjs +13 -0
  52. package/dist/icons/ArrowUp.mjs.map +1 -0
  53. package/dist/icons/{Warning.js → Missing.js} +3 -3
  54. package/dist/icons/{Warning.js.map → Missing.js.map} +1 -1
  55. package/dist/icons/{Warning.mjs → Missing.mjs} +3 -3
  56. package/dist/icons/{Warning.mjs.map → Missing.mjs.map} +1 -1
  57. package/dist/icons/Restore.js +17 -0
  58. package/dist/icons/Restore.js.map +1 -0
  59. package/dist/icons/Restore.mjs +15 -0
  60. package/dist/icons/Restore.mjs.map +1 -0
  61. package/dist/icons/Spinner.js +9 -3
  62. package/dist/icons/Spinner.js.map +1 -1
  63. package/dist/icons/Spinner.mjs +10 -4
  64. package/dist/icons/Spinner.mjs.map +1 -1
  65. package/dist/index.d.mts +74 -70
  66. package/dist/index.d.ts +74 -70
  67. package/dist/index.js +6 -0
  68. package/dist/index.js.map +1 -1
  69. package/dist/index.mjs +3 -0
  70. package/dist/index.mjs.map +1 -1
  71. package/dist/overrides.js +5 -6
  72. package/dist/overrides.js.map +1 -1
  73. package/dist/overrides.mjs +5 -6
  74. package/dist/overrides.mjs.map +1 -1
  75. package/dist/primitives/Composer/contexts.js +3 -17
  76. package/dist/primitives/Composer/contexts.js.map +1 -1
  77. package/dist/primitives/Composer/contexts.mjs +4 -15
  78. package/dist/primitives/Composer/contexts.mjs.map +1 -1
  79. package/dist/primitives/Composer/index.js +26 -185
  80. package/dist/primitives/Composer/index.js.map +1 -1
  81. package/dist/primitives/Composer/index.mjs +31 -188
  82. package/dist/primitives/Composer/index.mjs.map +1 -1
  83. package/dist/primitives/Composer/utils.js +0 -224
  84. package/dist/primitives/Composer/utils.js.map +1 -1
  85. package/dist/primitives/Composer/utils.mjs +1 -222
  86. package/dist/primitives/Composer/utils.mjs.map +1 -1
  87. package/dist/primitives/EmojiPicker/utils.js +2 -2
  88. package/dist/primitives/EmojiPicker/utils.js.map +1 -1
  89. package/dist/primitives/EmojiPicker/utils.mjs +1 -1
  90. package/dist/primitives/EmojiPicker/utils.mjs.map +1 -1
  91. package/dist/primitives/index.d.mts +3 -83
  92. package/dist/primitives/index.d.ts +3 -83
  93. package/dist/primitives/index.js +0 -4
  94. package/dist/primitives/index.js.map +1 -1
  95. package/dist/primitives/index.mjs +0 -2
  96. package/dist/primitives/index.mjs.map +1 -1
  97. package/dist/utils/chunk.js +12 -0
  98. package/dist/utils/chunk.js.map +1 -0
  99. package/dist/utils/chunk.mjs +10 -0
  100. package/dist/utils/chunk.mjs.map +1 -0
  101. package/dist/utils/intl.js +0 -6
  102. package/dist/utils/intl.js.map +1 -1
  103. package/dist/utils/intl.mjs +1 -6
  104. package/dist/utils/intl.mjs.map +1 -1
  105. package/dist/utils/use-initial.js +1 -2
  106. package/dist/utils/use-initial.js.map +1 -1
  107. package/dist/utils/use-initial.mjs +2 -3
  108. package/dist/utils/use-initial.mjs.map +1 -1
  109. package/dist/version.js +1 -1
  110. package/dist/version.js.map +1 -1
  111. package/dist/version.mjs +1 -1
  112. package/dist/version.mjs.map +1 -1
  113. package/package.json +16 -4
  114. package/src/styles/dark/index.css +0 -1
  115. package/src/styles/index.css +219 -325
  116. package/src/styles/utils.css +6 -44
  117. package/styles/dark/attributes.css +1 -1
  118. package/styles/dark/attributes.css.map +1 -1
  119. package/styles/dark/media-query.css +1 -1
  120. package/styles/dark/media-query.css.map +1 -1
  121. package/styles.css +1 -1
  122. package/styles.css.map +1 -1
  123. package/dist/components/internal/Attachment.js +0 -207
  124. package/dist/components/internal/Attachment.js.map +0 -1
  125. package/dist/components/internal/Attachment.mjs +0 -205
  126. package/dist/components/internal/Attachment.mjs.map +0 -1
  127. package/dist/icons/Attachment.js +0 -15
  128. package/dist/icons/Attachment.js.map +0 -1
  129. package/dist/icons/Attachment.mjs +0 -13
  130. package/dist/icons/Attachment.mjs.map +0 -1
  131. package/dist/primitives/FileSize.js +0 -33
  132. package/dist/primitives/FileSize.js.map +0 -1
  133. package/dist/primitives/FileSize.mjs +0 -31
  134. package/dist/primitives/FileSize.mjs.map +0 -1
  135. package/dist/utils/download.js +0 -14
  136. package/dist/utils/download.js.map +0 -1
  137. package/dist/utils/download.mjs +0 -12
  138. package/dist/utils/download.mjs.map +0 -1
  139. package/dist/utils/format-file-size.js +0 -45
  140. package/dist/utils/format-file-size.js.map +0 -1
  141. package/dist/utils/format-file-size.mjs +0 -43
  142. package/dist/utils/format-file-size.mjs.map +0 -1
@@ -4,19 +4,15 @@
4
4
  var core = require('@liveblocks/core');
5
5
  var react = require('@liveblocks/react');
6
6
  var React = require('react');
7
- var Attachment = require('../icons/Attachment.js');
8
7
  var Emoji = require('../icons/Emoji.js');
9
8
  var Mention = require('../icons/Mention.js');
10
9
  var Send = require('../icons/Send.js');
11
10
  var overrides = require('../overrides.js');
12
11
  var index = require('../primitives/Composer/index.js');
13
12
  var contexts = require('../primitives/Composer/contexts.js');
14
- var utils = require('../primitives/Composer/utils.js');
15
13
  var mentions = require('../slate/plugins/mentions.js');
16
14
  var classNames = require('../utils/class-names.js');
17
15
  var useControllableState = require('../utils/use-controllable-state.js');
18
- var useLayoutEffect = require('../utils/use-layout-effect.js');
19
- var Attachment$1 = require('./internal/Attachment.js');
20
16
  var Attribution = require('./internal/Attribution.js');
21
17
  var Avatar = require('./internal/Avatar.js');
22
18
  var Button = require('./internal/Button.js');
@@ -88,31 +84,6 @@ function ComposerInsertEmojiEditorAction({
88
84
  className: "lb-button-icon"
89
85
  })))));
90
86
  }
91
- function ComposerAttachFilesEditorAction({
92
- label,
93
- className,
94
- ...props
95
- }) {
96
- const preventDefault = React.useCallback((event) => {
97
- event.preventDefault();
98
- }, []);
99
- const stopPropagation = React.useCallback((event) => {
100
- event.stopPropagation();
101
- }, []);
102
- return /* @__PURE__ */ React.createElement(Tooltip.Tooltip, {
103
- content: label
104
- }, /* @__PURE__ */ React.createElement(index.AttachFiles, {
105
- asChild: true
106
- }, /* @__PURE__ */ React.createElement(Button.Button, {
107
- className: classNames.classNames("lb-composer-editor-action", className),
108
- onMouseDown: preventDefault,
109
- onClick: stopPropagation,
110
- "aria-label": label,
111
- ...props
112
- }, /* @__PURE__ */ React.createElement(Attachment.AttachmentIcon, {
113
- className: "lb-button-icon"
114
- }))));
115
- }
116
87
  function ComposerMention({ userId }) {
117
88
  return /* @__PURE__ */ React.createElement(index.Mention, {
118
89
  className: "lb-composer-mention"
@@ -145,184 +116,68 @@ function ComposerLink({ href, children }) {
145
116
  className: "lb-composer-link"
146
117
  }, children);
147
118
  }
148
- function ComposerFileAttachment({
149
- attachment,
150
- className,
151
- overrides,
152
- ...props
153
- }) {
154
- const { removeAttachment } = contexts.useComposer();
155
- const handleDeleteClick = React.useCallback(() => {
156
- removeAttachment(attachment.id);
157
- }, [attachment.id, removeAttachment]);
158
- return /* @__PURE__ */ React.createElement(Attachment$1.FileAttachment, {
159
- className: classNames.classNames("lb-composer-attachment", className),
160
- ...props,
161
- attachment,
162
- onDeleteClick: handleDeleteClick,
163
- preventFocusOnDelete: true,
164
- overrides
165
- });
166
- }
167
- function ComposerAttachments({
168
- overrides,
169
- className,
170
- ...props
171
- }) {
172
- const { attachments } = contexts.useComposer();
173
- if (attachments.length === 0) {
174
- return null;
175
- }
176
- return /* @__PURE__ */ React.createElement("div", {
177
- className: classNames.classNames("lb-composer-attachments", className),
178
- ...props
179
- }, attachments.map((attachment) => {
180
- return /* @__PURE__ */ React.createElement(ComposerFileAttachment, {
181
- key: attachment.id,
182
- attachment,
183
- overrides
184
- });
185
- }));
186
- }
187
119
  const editorComponents = {
188
120
  Mention: ComposerMention,
189
121
  MentionSuggestions: ComposerMentionSuggestions,
190
122
  Link: ComposerLink
191
123
  };
192
- function ComposerEditorContainer({
193
- showAttachments = true,
194
- showAttribution,
195
- defaultValue,
196
- isCollapsed,
197
- overrides: overrides$1,
198
- actions,
199
- autoFocus,
200
- hasResolveMentionSuggestions,
201
- onEmojiPickerOpenChange,
202
- onEmptyChange,
203
- onEditorClick
204
- }) {
205
- const ref = React.useRef(null);
206
- const { isEmpty } = contexts.useComposer();
207
- const $ = overrides.useOverrides(overrides$1);
208
- const ignoreDropAreaLeaveEvent = React.useCallback(
209
- (event) => {
210
- return Boolean(
211
- event.relatedTarget && event.target && event.relatedTarget === ref.current && ref.current?.contains(event.target)
212
- );
213
- },
214
- []
215
- );
216
- const [isDraggingOver, dropAreaProps] = utils.useComposerAttachmentsDropArea({
217
- ignoreLeaveEvent: ignoreDropAreaLeaveEvent
218
- });
219
- useLayoutEffect.useLayoutEffect(() => {
220
- onEmptyChange(isEmpty);
221
- }, [isEmpty, onEmptyChange]);
222
- const preventDefault = React.useCallback((event) => {
223
- event.preventDefault();
224
- }, []);
225
- const stopPropagation = React.useCallback((event) => {
226
- event.stopPropagation();
227
- }, []);
228
- return /* @__PURE__ */ React.createElement("div", {
229
- className: "lb-composer-editor-container",
230
- ref,
231
- ...dropAreaProps
232
- }, /* @__PURE__ */ React.createElement(index.Editor, {
233
- className: "lb-composer-editor",
234
- onClick: onEditorClick,
235
- placeholder: $.COMPOSER_PLACEHOLDER,
236
- defaultValue,
237
- autoFocus,
238
- components: editorComponents,
239
- dir: $.dir
240
- }), showAttachments && /* @__PURE__ */ React.createElement(ComposerAttachments, {
241
- overrides: overrides$1
242
- }), (!isCollapsed || isDraggingOver) && /* @__PURE__ */ React.createElement("div", {
243
- className: "lb-composer-footer"
244
- }, /* @__PURE__ */ React.createElement("div", {
245
- className: "lb-composer-editor-actions"
246
- }, hasResolveMentionSuggestions && /* @__PURE__ */ React.createElement(ComposerInsertMentionEditorAction, {
247
- label: $.COMPOSER_INSERT_MENTION
248
- }), /* @__PURE__ */ React.createElement(ComposerInsertEmojiEditorAction, {
249
- label: $.COMPOSER_INSERT_EMOJI,
250
- onPickerOpenChange: onEmojiPickerOpenChange
251
- }), showAttachments && /* @__PURE__ */ React.createElement(ComposerAttachFilesEditorAction, {
252
- label: $.COMPOSER_ATTACH_FILES
253
- })), showAttribution && /* @__PURE__ */ React.createElement(Attribution.Attribution, null), /* @__PURE__ */ React.createElement("div", {
254
- className: "lb-composer-actions"
255
- }, actions ?? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Tooltip.ShortcutTooltip, {
256
- content: $.COMPOSER_SEND,
257
- shortcut: /* @__PURE__ */ React.createElement(Tooltip.ShortcutTooltipKey, {
258
- name: "enter"
259
- })
260
- }, /* @__PURE__ */ React.createElement(index.Submit, {
261
- asChild: true
262
- }, /* @__PURE__ */ React.createElement(Button.Button, {
263
- onMouseDown: preventDefault,
264
- onClick: stopPropagation,
265
- className: "lb-composer-action",
266
- variant: "primary",
267
- "aria-label": $.COMPOSER_SEND
268
- }, /* @__PURE__ */ React.createElement(Send.SendIcon, null))))))), showAttachments && isDraggingOver && /* @__PURE__ */ React.createElement("div", {
269
- className: "lb-composer-attachments-drop-area"
270
- }, /* @__PURE__ */ React.createElement("div", {
271
- className: "lb-composer-attachments-drop-area-label"
272
- }, /* @__PURE__ */ React.createElement(Attachment.AttachmentIcon, null), $.COMPOSER_ATTACH_FILES)));
273
- }
274
- const Composer = React.forwardRef(
124
+ const ComposerWithContext = React.forwardRef(
275
125
  ({
276
- threadId,
277
- commentId,
278
- metadata,
279
126
  defaultValue,
280
- defaultAttachments,
281
- onComposerSubmit,
127
+ disabled,
128
+ autoFocus,
282
129
  collapsed: controlledCollapsed,
283
130
  defaultCollapsed,
284
131
  onCollapsedChange: controlledOnCollapsedChange,
285
- overrides: overrides$1,
286
132
  actions,
133
+ overrides: overrides$1,
134
+ showAttribution,
135
+ onFocus,
287
136
  onBlur,
288
137
  className,
289
- onFocus,
290
- autoFocus,
291
- disabled,
292
- showAttachments = true,
293
- showAttribution,
294
138
  ...props
295
139
  }, forwardedRef) => {
296
140
  const client = react.useClient();
297
- const createThread = react.useCreateThread();
298
- const createComment = react.useCreateComment();
299
- const editComment = react.useEditComment();
300
141
  const hasResolveMentionSuggestions = client[core.kInternal].resolveMentionSuggestions !== void 0;
301
- const isEmptyRef = React.useRef(true);
302
- const isEmojiPickerOpenRef = React.useRef(false);
142
+ const self = react.useSelf();
143
+ const isDisabled = React.useMemo(
144
+ () => disabled || (self ? !self.canComment : false),
145
+ [disabled, self?.canComment]
146
+ );
147
+ const { isEmpty } = contexts.useComposer();
303
148
  const $ = overrides.useOverrides(overrides$1);
304
- const [isCollapsed, onCollapsedChange] = useControllableState.useControllableState(
149
+ const [isEmojiPickerOpen, setEmojiPickerOpen] = React.useState(false);
150
+ const [collapsed, onCollapsedChange] = useControllableState.useControllableState(
305
151
  controlledCollapsed === void 0 && defaultCollapsed === void 0 ? false : controlledCollapsed,
306
152
  controlledOnCollapsedChange,
307
153
  defaultCollapsed
308
154
  );
309
- const setEmptyRef = React.useCallback((isEmpty) => {
310
- isEmptyRef.current = isEmpty;
155
+ const preventDefault = React.useCallback((event) => {
156
+ event.preventDefault();
311
157
  }, []);
312
- const setEmojiPickerOpenRef = React.useCallback((isEmojiPickerOpen) => {
313
- isEmojiPickerOpenRef.current = isEmojiPickerOpen;
158
+ const stopPropagation = React.useCallback((event) => {
159
+ event.stopPropagation();
314
160
  }, []);
161
+ const handleEditorClick = React.useCallback(
162
+ (event) => {
163
+ event.stopPropagation();
164
+ if (isEmpty) {
165
+ onCollapsedChange?.(false);
166
+ }
167
+ },
168
+ [isEmpty, onCollapsedChange]
169
+ );
315
170
  const handleFocus = React.useCallback(
316
171
  (event) => {
317
172
  onFocus?.(event);
318
173
  if (event.isDefaultPrevented()) {
319
174
  return;
320
175
  }
321
- if (isEmptyRef.current) {
176
+ if (isEmpty) {
322
177
  onCollapsedChange?.(false);
323
178
  }
324
179
  },
325
- [onCollapsedChange, onFocus]
180
+ [isEmpty, onCollapsedChange, onFocus]
326
181
  );
327
182
  const handleBlur = React.useCallback(
328
183
  (event) => {
@@ -330,24 +185,75 @@ const Composer = React.forwardRef(
330
185
  if (event.isDefaultPrevented()) {
331
186
  return;
332
187
  }
333
- const isOutside = !event.currentTarget.contains(
334
- event.relatedTarget ?? document.activeElement
335
- );
336
- if (isOutside && isEmptyRef.current && !isEmojiPickerOpenRef.current) {
188
+ const isOutside = !event.currentTarget.contains(event.relatedTarget);
189
+ if (isOutside && isEmpty && !isEmojiPickerOpen) {
337
190
  onCollapsedChange?.(true);
338
191
  }
339
192
  },
340
- [onBlur, onCollapsedChange]
341
- );
342
- const handleEditorClick = React.useCallback(
343
- (event) => {
344
- event.stopPropagation();
345
- if (isEmptyRef.current) {
346
- onCollapsedChange?.(false);
347
- }
348
- },
349
- [onCollapsedChange]
193
+ [isEmojiPickerOpen, isEmpty, onBlur, onCollapsedChange]
350
194
  );
195
+ return /* @__PURE__ */ React.createElement("form", {
196
+ className: classNames.classNames(
197
+ "lb-root lb-composer lb-composer-form",
198
+ className
199
+ ),
200
+ dir: $.dir,
201
+ ...props,
202
+ ref: forwardedRef,
203
+ "data-collapsed": collapsed ? "" : void 0,
204
+ onFocus: handleFocus,
205
+ onBlur: handleBlur
206
+ }, /* @__PURE__ */ React.createElement(index.Editor, {
207
+ className: "lb-composer-editor",
208
+ onClick: handleEditorClick,
209
+ placeholder: $.COMPOSER_PLACEHOLDER,
210
+ defaultValue,
211
+ disabled: isDisabled,
212
+ autoFocus,
213
+ components: editorComponents,
214
+ dir: $.dir
215
+ }), !collapsed && /* @__PURE__ */ React.createElement("div", {
216
+ className: "lb-composer-footer"
217
+ }, /* @__PURE__ */ React.createElement("div", {
218
+ className: "lb-composer-editor-actions"
219
+ }, hasResolveMentionSuggestions && /* @__PURE__ */ React.createElement(ComposerInsertMentionEditorAction, {
220
+ label: $.COMPOSER_INSERT_MENTION,
221
+ disabled: isDisabled
222
+ }), /* @__PURE__ */ React.createElement(ComposerInsertEmojiEditorAction, {
223
+ label: $.COMPOSER_INSERT_EMOJI,
224
+ onPickerOpenChange: setEmojiPickerOpen,
225
+ disabled: isDisabled
226
+ })), showAttribution && /* @__PURE__ */ React.createElement(Attribution.Attribution, null), /* @__PURE__ */ React.createElement("div", {
227
+ className: "lb-composer-actions"
228
+ }, actions ?? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Tooltip.ShortcutTooltip, {
229
+ content: $.COMPOSER_SEND,
230
+ shortcut: /* @__PURE__ */ React.createElement(Tooltip.ShortcutTooltipKey, {
231
+ name: "enter"
232
+ })
233
+ }, /* @__PURE__ */ React.createElement(index.Submit, {
234
+ disabled: isDisabled,
235
+ asChild: true
236
+ }, /* @__PURE__ */ React.createElement(Button.Button, {
237
+ onMouseDown: preventDefault,
238
+ onClick: stopPropagation,
239
+ className: "lb-composer-action",
240
+ variant: "primary",
241
+ "aria-label": $.COMPOSER_SEND
242
+ }, /* @__PURE__ */ React.createElement(Send.SendIcon, null))))))));
243
+ }
244
+ );
245
+ const Composer = React.forwardRef(
246
+ ({
247
+ threadId,
248
+ commentId,
249
+ metadata,
250
+ onComposerSubmit,
251
+ onFocus,
252
+ ...props
253
+ }, forwardedRef) => {
254
+ const createThread = react.useCreateThread();
255
+ const createComment = react.useCreateComment();
256
+ const editComment = react.useEditComment();
351
257
  const handleCommentSubmit = React.useCallback(
352
258
  (comment, event) => {
353
259
  onComposerSubmit?.(comment, event);
@@ -358,20 +264,17 @@ const Composer = React.forwardRef(
358
264
  editComment({
359
265
  commentId,
360
266
  threadId,
361
- body: comment.body,
362
- attachments: comment.attachments
267
+ body: comment.body
363
268
  });
364
269
  } else if (threadId) {
365
270
  createComment({
366
271
  threadId,
367
- body: comment.body,
368
- attachments: comment.attachments
272
+ body: comment.body
369
273
  });
370
274
  } else {
371
275
  createThread({
372
276
  body: comment.body,
373
- metadata: metadata ?? {},
374
- attachments: comment.attachments
277
+ metadata: metadata ?? {}
375
278
  });
376
279
  }
377
280
  },
@@ -387,30 +290,11 @@ const Composer = React.forwardRef(
387
290
  );
388
291
  return /* @__PURE__ */ React.createElement(TooltipPrimitive.TooltipProvider, null, /* @__PURE__ */ React.createElement(index.Form, {
389
292
  onComposerSubmit: handleCommentSubmit,
390
- className: classNames.classNames(
391
- "lb-root lb-composer lb-composer-form",
392
- className
393
- ),
394
- dir: $.dir,
293
+ onFocus,
294
+ asChild: true
295
+ }, /* @__PURE__ */ React.createElement(ComposerWithContext, {
395
296
  ...props,
396
- ref: forwardedRef,
397
- "data-collapsed": isCollapsed ? "" : void 0,
398
- onFocus: handleFocus,
399
- onBlur: handleBlur,
400
- disabled,
401
- defaultAttachments
402
- }, /* @__PURE__ */ React.createElement(ComposerEditorContainer, {
403
- defaultValue,
404
- actions,
405
- overrides: overrides$1,
406
- isCollapsed,
407
- showAttachments,
408
- showAttribution,
409
- hasResolveMentionSuggestions,
410
- onEmptyChange: setEmptyRef,
411
- onEmojiPickerOpenChange: setEmojiPickerOpenRef,
412
- onEditorClick: handleEditorClick,
413
- autoFocus
297
+ ref: forwardedRef
414
298
  })));
415
299
  }
416
300
  );
@@ -1 +1 @@
1
- {"version":3,"file":"Composer.js","sources":["../../src/components/Composer.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n BaseMetadata,\n CommentAttachment,\n CommentMixedAttachment,\n DM,\n} from \"@liveblocks/core\";\nimport { kInternal } from \"@liveblocks/core\";\nimport {\n useClient,\n useCreateComment,\n useCreateThread,\n useEditComment,\n} from \"@liveblocks/react\";\nimport type {\n ComponentPropsWithoutRef,\n DragEvent,\n FocusEvent,\n FormEvent,\n ForwardedRef,\n MouseEvent,\n ReactNode,\n RefAttributes,\n SyntheticEvent,\n} from \"react\";\nimport React, { forwardRef, useCallback, useRef } from \"react\";\n\nimport { AttachmentIcon } from \"../icons/Attachment\";\nimport { EmojiIcon } from \"../icons/Emoji\";\nimport { MentionIcon } from \"../icons/Mention\";\nimport { SendIcon } from \"../icons/Send\";\nimport type { ComposerOverrides, GlobalOverrides } from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport * as ComposerPrimitive from \"../primitives/Composer\";\nimport { useComposer } from \"../primitives/Composer/contexts\";\nimport type {\n ComposerEditorComponents,\n ComposerEditorLinkProps,\n ComposerEditorMentionProps,\n ComposerEditorMentionSuggestionsProps,\n ComposerEditorProps,\n ComposerFormProps,\n ComposerSubmitComment,\n} from \"../primitives/Composer/types\";\nimport { useComposerAttachmentsDropArea } from \"../primitives/Composer/utils\";\nimport { MENTION_CHARACTER } from \"../slate/plugins/mentions\";\nimport { classNames } from \"../utils/class-names\";\nimport { useControllableState } from \"../utils/use-controllable-state\";\nimport { useLayoutEffect } from \"../utils/use-layout-effect\";\nimport { FileAttachment } from \"./internal/Attachment\";\nimport { Attribution } from \"./internal/Attribution\";\nimport { Avatar } from \"./internal/Avatar\";\nimport { Button } from \"./internal/Button\";\nimport type { EmojiPickerProps } from \"./internal/EmojiPicker\";\nimport { EmojiPicker, EmojiPickerTrigger } from \"./internal/EmojiPicker\";\nimport {\n ShortcutTooltip,\n ShortcutTooltipKey,\n Tooltip,\n TooltipProvider,\n} from \"./internal/Tooltip\";\nimport { User } from \"./internal/User\";\n\ninterface EditorActionProps extends ComponentPropsWithoutRef<\"button\"> {\n label: string;\n}\n\ninterface EmojiEditorActionProps extends EditorActionProps {\n onPickerOpenChange?: EmojiPickerProps[\"onOpenChange\"];\n}\n\ntype ComposerCreateThreadProps<M extends BaseMetadata> = {\n threadId?: never;\n commentId?: never;\n\n /**\n * The metadata of the thread to create.\n */\n metadata?: M;\n};\n\ntype ComposerCreateCommentProps = {\n /**\n * The ID of the thread to reply to.\n */\n threadId: string;\n commentId?: never;\n metadata?: never;\n};\n\ntype ComposerEditCommentProps = {\n /**\n * The ID of the thread to edit a comment in.\n */\n threadId: string;\n\n /**\n * The ID of the comment to edit.\n */\n commentId: string;\n metadata?: never;\n};\n\nexport type ComposerProps<M extends BaseMetadata = DM> = Omit<\n ComponentPropsWithoutRef<\"form\">,\n \"defaultValue\"\n> &\n (\n | ComposerCreateThreadProps<M>\n | ComposerCreateCommentProps\n | ComposerEditCommentProps\n ) & {\n /**\n * The event handler called when the composer is submitted.\n */\n onComposerSubmit?: (\n comment: ComposerSubmitComment,\n event: FormEvent<HTMLFormElement>\n ) => Promise<void> | void;\n\n /**\n * The composer's initial value.\n */\n defaultValue?: ComposerEditorProps[\"defaultValue\"];\n\n /**\n * The composer's initial attachments.\n */\n defaultAttachments?: CommentAttachment[];\n\n /**\n * Whether the composer is collapsed. Setting a value will make the composer controlled.\n */\n collapsed?: boolean;\n\n /**\n * The event handler called when the collapsed state of the composer changes.\n */\n onCollapsedChange?: (collapsed: boolean) => void;\n\n /**\n * Whether the composer is initially collapsed. Setting a value will make the composer uncontrolled.\n */\n defaultCollapsed?: boolean;\n\n /**\n * Whether to show and allow adding attachments.\n */\n showAttachments?: boolean;\n\n /**\n * Whether the composer is disabled.\n */\n disabled?: ComposerFormProps[\"disabled\"];\n\n /**\n * Whether to focus the composer on mount.\n */\n autoFocus?: ComposerEditorProps[\"autoFocus\"];\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n\n /**\n * @internal\n */\n actions?: ReactNode;\n\n /**\n * @internal\n */\n showAttribution?: boolean;\n };\n\ninterface ComposerEditorContainerProps\n extends Pick<\n ComposerProps,\n | \"defaultValue\"\n | \"showAttachments\"\n | \"showAttribution\"\n | \"overrides\"\n | \"actions\"\n | \"autoFocus\"\n > {\n isCollapsed: boolean | undefined;\n onEmptyChange: (isEmpty: boolean) => void;\n hasResolveMentionSuggestions: boolean;\n onEmojiPickerOpenChange: (isOpen: boolean) => void;\n onEditorClick: (event: MouseEvent<HTMLDivElement>) => void;\n}\n\nfunction ComposerInsertMentionEditorAction({\n label,\n className,\n onClick,\n ...props\n}: EditorActionProps) {\n const { createMention } = useComposer();\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n event.stopPropagation();\n createMention();\n }\n },\n [createMention, onClick]\n );\n\n return (\n <Tooltip content={label}>\n <Button\n className={classNames(\"lb-composer-editor-action\", className)}\n onMouseDown={preventDefault}\n onClick={handleClick}\n aria-label={label}\n {...props}\n >\n <MentionIcon className=\"lb-button-icon\" />\n </Button>\n </Tooltip>\n );\n}\n\nfunction ComposerInsertEmojiEditorAction({\n label,\n onPickerOpenChange,\n className,\n ...props\n}: EmojiEditorActionProps) {\n const { insertText } = useComposer();\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <EmojiPicker onEmojiSelect={insertText} onOpenChange={onPickerOpenChange}>\n <Tooltip content={label}>\n <EmojiPickerTrigger asChild>\n <Button\n className={classNames(\"lb-composer-editor-action\", className)}\n onMouseDown={preventDefault}\n onClick={stopPropagation}\n aria-label={label}\n {...props}\n >\n <EmojiIcon className=\"lb-button-icon\" />\n </Button>\n </EmojiPickerTrigger>\n </Tooltip>\n </EmojiPicker>\n );\n}\n\nfunction ComposerAttachFilesEditorAction({\n label,\n className,\n ...props\n}: EditorActionProps) {\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <Tooltip content={label}>\n <ComposerPrimitive.AttachFiles asChild>\n <Button\n className={classNames(\"lb-composer-editor-action\", className)}\n onMouseDown={preventDefault}\n onClick={stopPropagation}\n aria-label={label}\n {...props}\n >\n <AttachmentIcon className=\"lb-button-icon\" />\n </Button>\n </ComposerPrimitive.AttachFiles>\n </Tooltip>\n );\n}\n\nfunction ComposerMention({ userId }: ComposerEditorMentionProps) {\n return (\n <ComposerPrimitive.Mention className=\"lb-composer-mention\">\n {MENTION_CHARACTER}\n <User userId={userId} />\n </ComposerPrimitive.Mention>\n );\n}\n\nfunction ComposerMentionSuggestions({\n userIds,\n}: ComposerEditorMentionSuggestionsProps) {\n return userIds.length > 0 ? (\n <ComposerPrimitive.Suggestions className=\"lb-root lb-portal lb-elevation lb-composer-suggestions lb-composer-mention-suggestions\">\n <ComposerPrimitive.SuggestionsList className=\"lb-composer-suggestions-list lb-composer-mention-suggestions-list\">\n {userIds.map((userId) => (\n <ComposerPrimitive.SuggestionsListItem\n key={userId}\n className=\"lb-composer-suggestions-list-item lb-composer-mention-suggestion\"\n value={userId}\n >\n <Avatar\n userId={userId}\n className=\"lb-composer-mention-suggestion-avatar\"\n />\n <User\n userId={userId}\n className=\"lb-composer-mention-suggestion-user\"\n />\n </ComposerPrimitive.SuggestionsListItem>\n ))}\n </ComposerPrimitive.SuggestionsList>\n </ComposerPrimitive.Suggestions>\n ) : null;\n}\n\nfunction ComposerLink({ href, children }: ComposerEditorLinkProps) {\n return (\n <ComposerPrimitive.Link href={href} className=\"lb-composer-link\">\n {children}\n </ComposerPrimitive.Link>\n );\n}\n\ninterface ComposerAttachmentsProps extends ComponentPropsWithoutRef<\"div\"> {\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n}\n\ninterface ComposerFileAttachmentProps extends ComponentPropsWithoutRef<\"div\"> {\n attachment: CommentMixedAttachment;\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n}\n\nfunction ComposerFileAttachment({\n attachment,\n className,\n overrides,\n ...props\n}: ComposerFileAttachmentProps) {\n const { removeAttachment } = useComposer();\n\n const handleDeleteClick = useCallback(() => {\n removeAttachment(attachment.id);\n }, [attachment.id, removeAttachment]);\n\n return (\n <FileAttachment\n className={classNames(\"lb-composer-attachment\", className)}\n {...props}\n attachment={attachment}\n onDeleteClick={handleDeleteClick}\n preventFocusOnDelete\n overrides={overrides}\n />\n );\n}\n\nfunction ComposerAttachments({\n overrides,\n className,\n ...props\n}: ComposerAttachmentsProps) {\n const { attachments } = useComposer();\n\n if (attachments.length === 0) {\n return null;\n }\n\n return (\n <div\n className={classNames(\"lb-composer-attachments\", className)}\n {...props}\n >\n {attachments.map((attachment) => {\n return (\n <ComposerFileAttachment\n key={attachment.id}\n attachment={attachment}\n overrides={overrides}\n />\n );\n })}\n </div>\n );\n}\n\nconst editorComponents: ComposerEditorComponents = {\n Mention: ComposerMention,\n MentionSuggestions: ComposerMentionSuggestions,\n Link: ComposerLink,\n};\n\nfunction ComposerEditorContainer({\n showAttachments = true,\n showAttribution,\n defaultValue,\n isCollapsed,\n overrides,\n actions,\n autoFocus,\n hasResolveMentionSuggestions,\n onEmojiPickerOpenChange,\n onEmptyChange,\n onEditorClick,\n}: ComposerEditorContainerProps) {\n const ref = useRef<HTMLDivElement>(null);\n const { isEmpty } = useComposer();\n const $ = useOverrides(overrides);\n const ignoreDropAreaLeaveEvent = useCallback(\n (event: DragEvent<HTMLDivElement>) => {\n return Boolean(\n event.relatedTarget &&\n event.target &&\n event.relatedTarget === ref.current &&\n ref.current?.contains(event.target as HTMLElement)\n );\n },\n []\n );\n const [isDraggingOver, dropAreaProps] = useComposerAttachmentsDropArea({\n ignoreLeaveEvent: ignoreDropAreaLeaveEvent,\n });\n\n useLayoutEffect(() => {\n onEmptyChange(isEmpty);\n }, [isEmpty, onEmptyChange]);\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <div className=\"lb-composer-editor-container\" ref={ref} {...dropAreaProps}>\n <ComposerPrimitive.Editor\n className=\"lb-composer-editor\"\n onClick={onEditorClick}\n placeholder={$.COMPOSER_PLACEHOLDER}\n defaultValue={defaultValue}\n autoFocus={autoFocus}\n components={editorComponents}\n dir={$.dir}\n />\n {showAttachments && <ComposerAttachments overrides={overrides} />}\n {(!isCollapsed || isDraggingOver) && (\n <div className=\"lb-composer-footer\">\n <div className=\"lb-composer-editor-actions\">\n {hasResolveMentionSuggestions && (\n <ComposerInsertMentionEditorAction\n label={$.COMPOSER_INSERT_MENTION}\n />\n )}\n <ComposerInsertEmojiEditorAction\n label={$.COMPOSER_INSERT_EMOJI}\n onPickerOpenChange={onEmojiPickerOpenChange}\n />\n {showAttachments && (\n <ComposerAttachFilesEditorAction\n label={$.COMPOSER_ATTACH_FILES}\n />\n )}\n </div>\n {showAttribution && <Attribution />}\n <div className=\"lb-composer-actions\">\n {actions ?? (\n <>\n <ShortcutTooltip\n content={$.COMPOSER_SEND}\n shortcut={<ShortcutTooltipKey name=\"enter\" />}\n >\n <ComposerPrimitive.Submit asChild>\n <Button\n onMouseDown={preventDefault}\n onClick={stopPropagation}\n className=\"lb-composer-action\"\n variant=\"primary\"\n aria-label={$.COMPOSER_SEND}\n >\n <SendIcon />\n </Button>\n </ComposerPrimitive.Submit>\n </ShortcutTooltip>\n </>\n )}\n </div>\n </div>\n )}\n {showAttachments && isDraggingOver && (\n <div className=\"lb-composer-attachments-drop-area\">\n <div className=\"lb-composer-attachments-drop-area-label\">\n <AttachmentIcon />\n {$.COMPOSER_ATTACH_FILES}\n </div>\n </div>\n )}\n </div>\n );\n}\n\n/**\n * Displays a composer to create comments.\n *\n * @example\n * <Composer />\n */\nexport const Composer = forwardRef(\n <M extends BaseMetadata = DM>(\n {\n threadId,\n commentId,\n metadata,\n defaultValue,\n defaultAttachments,\n onComposerSubmit,\n collapsed: controlledCollapsed,\n defaultCollapsed,\n onCollapsedChange: controlledOnCollapsedChange,\n overrides,\n actions,\n onBlur,\n className,\n onFocus,\n autoFocus,\n disabled,\n showAttachments = true,\n showAttribution,\n ...props\n }: ComposerProps<M>,\n forwardedRef: ForwardedRef<HTMLFormElement>\n ) => {\n const client = useClient();\n const createThread = useCreateThread();\n const createComment = useCreateComment();\n const editComment = useEditComment();\n const hasResolveMentionSuggestions =\n client[kInternal].resolveMentionSuggestions !== undefined;\n const isEmptyRef = useRef(true);\n const isEmojiPickerOpenRef = useRef(false);\n const $ = useOverrides(overrides);\n const [isCollapsed, onCollapsedChange] = useControllableState(\n // If the composer is neither controlled nor uncontrolled, it defaults to controlled as uncollapsed.\n controlledCollapsed === undefined && defaultCollapsed === undefined\n ? false\n : controlledCollapsed,\n controlledOnCollapsedChange,\n defaultCollapsed\n );\n\n const setEmptyRef = useCallback((isEmpty: boolean) => {\n isEmptyRef.current = isEmpty;\n }, []);\n\n const setEmojiPickerOpenRef = useCallback((isEmojiPickerOpen: boolean) => {\n isEmojiPickerOpenRef.current = isEmojiPickerOpen;\n }, []);\n\n const handleFocus = useCallback(\n (event: FocusEvent<HTMLFormElement>) => {\n onFocus?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n if (isEmptyRef.current) {\n onCollapsedChange?.(false);\n }\n },\n [onCollapsedChange, onFocus]\n );\n\n const handleBlur = useCallback(\n (event: FocusEvent<HTMLFormElement>) => {\n onBlur?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n const isOutside = !event.currentTarget.contains(\n event.relatedTarget ?? document.activeElement\n );\n\n // TODO: Handle \"delete\" buttons on attachments (hide them when the composer is collapsed)\n if (isOutside && isEmptyRef.current && !isEmojiPickerOpenRef.current) {\n onCollapsedChange?.(true);\n }\n },\n [onBlur, onCollapsedChange]\n );\n\n const handleEditorClick = useCallback(\n (event: MouseEvent<HTMLDivElement>) => {\n event.stopPropagation();\n\n if (isEmptyRef.current) {\n onCollapsedChange?.(false);\n }\n },\n [onCollapsedChange]\n );\n\n const handleCommentSubmit = useCallback(\n (comment: ComposerSubmitComment, event: FormEvent<HTMLFormElement>) => {\n onComposerSubmit?.(comment, event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n if (commentId && threadId) {\n editComment({\n commentId,\n threadId,\n body: comment.body,\n attachments: comment.attachments,\n });\n } else if (threadId) {\n createComment({\n threadId,\n body: comment.body,\n attachments: comment.attachments,\n });\n } else {\n createThread({\n body: comment.body,\n metadata: metadata ?? {},\n attachments: comment.attachments,\n });\n }\n },\n [\n commentId,\n createComment,\n createThread,\n editComment,\n metadata,\n onComposerSubmit,\n threadId,\n ]\n );\n\n return (\n <TooltipProvider>\n <ComposerPrimitive.Form\n onComposerSubmit={handleCommentSubmit}\n className={classNames(\n \"lb-root lb-composer lb-composer-form\",\n className\n )}\n dir={$.dir}\n {...props}\n ref={forwardedRef}\n data-collapsed={isCollapsed ? \"\" : undefined}\n onFocus={handleFocus}\n onBlur={handleBlur}\n disabled={disabled}\n defaultAttachments={defaultAttachments}\n >\n <ComposerEditorContainer\n defaultValue={defaultValue}\n actions={actions}\n overrides={overrides}\n isCollapsed={isCollapsed}\n showAttachments={showAttachments}\n showAttribution={showAttribution}\n hasResolveMentionSuggestions={hasResolveMentionSuggestions}\n onEmptyChange={setEmptyRef}\n onEmojiPickerOpenChange={setEmojiPickerOpenRef}\n onEditorClick={handleEditorClick}\n autoFocus={autoFocus}\n />\n </ComposerPrimitive.Form>\n </TooltipProvider>\n );\n }\n) as <M extends BaseMetadata = DM>(\n props: ComposerProps<M> & RefAttributes<HTMLFormElement>\n) => JSX.Element;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkMA;AAA2C;AACzC;AACA;AACA;AAEF;AACE;AAEA;AACE;AAAqB;AAGvB;AAAoB;AAEhB;AAEA;AACE;AACA;AAAc;AAChB;AACF;AACuB;AAGzB;AACG;AAAiB;AACf;AAC6D;AAC/C;AACJ;AACG;AACR;AAEH;AAAsB;AAI/B;AAEA;AAAyC;AACvC;AACA;AACA;AAEF;AACE;AAEA;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AACG;AAA2B;AAA0B;AACnD;AAAiB;AACf;AAA0B;AACxB;AAC6D;AAC/C;AACJ;AACG;AACR;AAEH;AAAoB;AAMjC;AAEA;AAAyC;AACvC;AACA;AAEF;AACE;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AACG;AAAiB;AACf;AAAqC;AACnC;AAC6D;AAC/C;AACJ;AACG;AACR;AAEH;AAAyB;AAKpC;AAEA;AACE;AACG;AAAoC;AAElC;AAAK;AAGZ;AAEA;AAAoC;AAEpC;AACE;AACG;AAAwC;AACtC;AAA4C;AAExC;AACM;AACK;AACH;AAEN;AACC;AACU;AAEX;AACC;AACU;AAOxB;AAEA;AACE;AACG;AAAuB;AAAsB;AAIlD;AAWA;AAAgC;AAC9B;AACA;AACA;AAEF;AACE;AAEA;AACE;AAA8B;AAGhC;AACG;AAC0D;AACrD;AACJ;AACe;AACK;AACpB;AAGN;AAEA;AAA6B;AAC3B;AACA;AAEF;AACE;AAEA;AACE;AAAO;AAGT;AACG;AAC2D;AACtD;AAGF;AACG;AACiB;AAChB;AACA;AACF;AAKV;AAEA;AAAmD;AACxC;AACW;AAEtB;AAEA;AAAiC;AACb;AAClB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEF;AACE;AACA;AACA;AACA;AAAiC;AAE7B;AAAO;AAI8C;AACrD;AACF;AACC;AAEH;AAAuE;AACnD;AAGpB;AACE;AAAqB;AAGvB;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AACG;AAAc;AAA+B;AAAc;AACzD;AACW;AACD;AACM;AACf;AACA;AACY;AACL;AAEY;AAAoB;AAEtC;AAAc;AACZ;AAAc;AAEV;AACU;AAGZ;AACU;AACW;AAGnB;AACU;AAKd;AAAc;AAGR;AACY;AACA;AAAwB;AAAQ;AAE1C;AAAgC;AAC9B;AACc;AACJ;AACC;AACF;AACM;AAY3B;AAAc;AACZ;AAAc;AAQzB;AAQO;AAAiB;AAEpB;AACE;AACA;AACA;AACA;AACA;AACA;AACW;AACX;AACmB;AACnB;AACA;AACA;AACA;AACA;AACA;AACA;AACkB;AAClB;AACG;AAIL;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAyC;AAInC;AACJ;AACA;AAGF;AACE;AAAqB;AAGvB;AACE;AAA+B;AAGjC;AAAoB;AAEhB;AAEA;AACE;AAAA;AAGF;AACE;AAAyB;AAC3B;AACF;AAC2B;AAG7B;AAAmB;AAEf;AAEA;AACE;AAAA;AAGF;AAAuC;AACL;AAIlC;AACE;AAAwB;AAC1B;AACF;AAC0B;AAG5B;AAA0B;AAEtB;AAEA;AACE;AAAyB;AAC3B;AACF;AACkB;AAGpB;AAA4B;AAExB;AAEA;AACE;AAAA;AAGF;AACE;AAAY;AACV;AACA;AACc;AACO;AACtB;AAED;AAAc;AACZ;AACc;AACO;AACtB;AAED;AAAa;AACG;AACS;AACF;AACtB;AACH;AACF;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAGF;AAEK;AACmB;AACP;AACT;AACA;AACF;AACO;AACH;AACC;AAC8B;AAC1B;AACD;AACR;AACA;AAEC;AACC;AACA;AACA;AACA;AACA;AACA;AACA;AACe;AACU;AACV;AACf;AAGN;AAGN;;"}
1
+ {"version":3,"file":"Composer.js","sources":["../../src/components/Composer.tsx"],"sourcesContent":["\"use client\";\n\nimport type { BaseMetadata, DM } from \"@liveblocks/core\";\nimport { kInternal } from \"@liveblocks/core\";\nimport {\n useClient,\n useCreateComment,\n useCreateThread,\n useEditComment,\n useSelf,\n} from \"@liveblocks/react\";\nimport type {\n ComponentPropsWithoutRef,\n FocusEvent,\n FormEvent,\n ForwardedRef,\n MouseEvent,\n ReactNode,\n RefAttributes,\n SyntheticEvent,\n} from \"react\";\nimport React, { forwardRef, useCallback, useMemo, useState } from \"react\";\n\nimport { EmojiIcon } from \"../icons/Emoji\";\nimport { MentionIcon } from \"../icons/Mention\";\nimport { SendIcon } from \"../icons/Send\";\nimport type { ComposerOverrides, GlobalOverrides } from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport * as ComposerPrimitive from \"../primitives/Composer\";\nimport { useComposer } from \"../primitives/Composer/contexts\";\nimport type {\n ComposerEditorComponents,\n ComposerEditorLinkProps,\n ComposerEditorMentionProps,\n ComposerEditorMentionSuggestionsProps,\n ComposerEditorProps,\n ComposerSubmitComment,\n} from \"../primitives/Composer/types\";\nimport { MENTION_CHARACTER } from \"../slate/plugins/mentions\";\nimport { classNames } from \"../utils/class-names\";\nimport { useControllableState } from \"../utils/use-controllable-state\";\nimport { Attribution } from \"./internal/Attribution\";\nimport { Avatar } from \"./internal/Avatar\";\nimport { Button } from \"./internal/Button\";\nimport type { EmojiPickerProps } from \"./internal/EmojiPicker\";\nimport { EmojiPicker, EmojiPickerTrigger } from \"./internal/EmojiPicker\";\nimport {\n ShortcutTooltip,\n ShortcutTooltipKey,\n Tooltip,\n TooltipProvider,\n} from \"./internal/Tooltip\";\nimport { User } from \"./internal/User\";\n\ninterface EditorActionProps extends ComponentPropsWithoutRef<\"button\"> {\n label: string;\n}\n\ninterface EmojiEditorActionProps extends EditorActionProps {\n onPickerOpenChange?: EmojiPickerProps[\"onOpenChange\"];\n}\n\ntype ComposerCreateThreadProps<M extends BaseMetadata> = {\n threadId?: never;\n commentId?: never;\n\n /**\n * The metadata of the thread to create.\n */\n metadata?: M;\n};\n\ntype ComposerCreateCommentProps = {\n /**\n * The ID of the thread to reply to.\n */\n threadId: string;\n commentId?: never;\n metadata?: never;\n};\n\ntype ComposerEditCommentProps = {\n /**\n * The ID of the thread to edit a comment in.\n */\n threadId: string;\n\n /**\n * The ID of the comment to edit.\n */\n commentId: string;\n metadata?: never;\n};\n\nexport type ComposerProps<M extends BaseMetadata = DM> = Omit<\n ComponentPropsWithoutRef<\"form\">,\n \"defaultValue\"\n> &\n (\n | ComposerCreateThreadProps<M>\n | ComposerCreateCommentProps\n | ComposerEditCommentProps\n ) & {\n /**\n * The event handler called when the composer is submitted.\n */\n onComposerSubmit?: (\n comment: ComposerSubmitComment,\n event: FormEvent<HTMLFormElement>\n ) => Promise<void> | void;\n\n /**\n * The composer's initial value.\n */\n defaultValue?: ComposerEditorProps[\"defaultValue\"];\n\n /**\n * Whether the composer is collapsed. Setting a value will make the composer controlled.\n */\n collapsed?: boolean;\n\n /**\n * The event handler called when the collapsed state of the composer changes.\n */\n onCollapsedChange?: (collapsed: boolean) => void;\n\n /**\n * Whether the composer is initially collapsed. Setting a value will make the composer uncontrolled.\n */\n defaultCollapsed?: boolean;\n\n /**\n * Whether the composer is disabled.\n */\n disabled?: ComposerEditorProps[\"disabled\"];\n\n /**\n * Whether to focus the composer on mount.\n */\n autoFocus?: ComposerEditorProps[\"autoFocus\"];\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides & ComposerOverrides>;\n\n /**\n * @internal\n */\n actions?: ReactNode;\n\n /**\n * @internal\n */\n showAttribution?: boolean;\n };\n\nfunction ComposerInsertMentionEditorAction({\n label,\n className,\n onClick,\n ...props\n}: EditorActionProps) {\n const { createMention } = useComposer();\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n event.stopPropagation();\n createMention();\n }\n },\n [createMention, onClick]\n );\n\n return (\n <Tooltip content={label}>\n <Button\n className={classNames(\"lb-composer-editor-action\", className)}\n onMouseDown={preventDefault}\n onClick={handleClick}\n aria-label={label}\n {...props}\n >\n <MentionIcon className=\"lb-button-icon\" />\n </Button>\n </Tooltip>\n );\n}\n\nfunction ComposerInsertEmojiEditorAction({\n label,\n onPickerOpenChange,\n className,\n ...props\n}: EmojiEditorActionProps) {\n const { insertText } = useComposer();\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <EmojiPicker onEmojiSelect={insertText} onOpenChange={onPickerOpenChange}>\n <Tooltip content={label}>\n <EmojiPickerTrigger asChild>\n <Button\n className={classNames(\"lb-composer-editor-action\", className)}\n onMouseDown={preventDefault}\n onClick={stopPropagation}\n aria-label={label}\n {...props}\n >\n <EmojiIcon className=\"lb-button-icon\" />\n </Button>\n </EmojiPickerTrigger>\n </Tooltip>\n </EmojiPicker>\n );\n}\n\nfunction ComposerMention({ userId }: ComposerEditorMentionProps) {\n return (\n <ComposerPrimitive.Mention className=\"lb-composer-mention\">\n {MENTION_CHARACTER}\n <User userId={userId} />\n </ComposerPrimitive.Mention>\n );\n}\n\nfunction ComposerMentionSuggestions({\n userIds,\n}: ComposerEditorMentionSuggestionsProps) {\n return userIds.length > 0 ? (\n <ComposerPrimitive.Suggestions className=\"lb-root lb-portal lb-elevation lb-composer-suggestions lb-composer-mention-suggestions\">\n <ComposerPrimitive.SuggestionsList className=\"lb-composer-suggestions-list lb-composer-mention-suggestions-list\">\n {userIds.map((userId) => (\n <ComposerPrimitive.SuggestionsListItem\n key={userId}\n className=\"lb-composer-suggestions-list-item lb-composer-mention-suggestion\"\n value={userId}\n >\n <Avatar\n userId={userId}\n className=\"lb-composer-mention-suggestion-avatar\"\n />\n <User\n userId={userId}\n className=\"lb-composer-mention-suggestion-user\"\n />\n </ComposerPrimitive.SuggestionsListItem>\n ))}\n </ComposerPrimitive.SuggestionsList>\n </ComposerPrimitive.Suggestions>\n ) : null;\n}\n\nfunction ComposerLink({ href, children }: ComposerEditorLinkProps) {\n return (\n <ComposerPrimitive.Link href={href} className=\"lb-composer-link\">\n {children}\n </ComposerPrimitive.Link>\n );\n}\n\nconst editorComponents: ComposerEditorComponents = {\n Mention: ComposerMention,\n MentionSuggestions: ComposerMentionSuggestions,\n Link: ComposerLink,\n};\n\nconst ComposerWithContext = forwardRef<\n HTMLFormElement,\n Omit<ComposerProps, \"threadId\" | \"commentId\" | \"onComposerSubmit\">\n>(\n (\n {\n defaultValue,\n disabled,\n autoFocus,\n collapsed: controlledCollapsed,\n defaultCollapsed,\n onCollapsedChange: controlledOnCollapsedChange,\n actions,\n overrides,\n showAttribution,\n onFocus,\n onBlur,\n className,\n ...props\n },\n forwardedRef\n ) => {\n const client = useClient();\n const hasResolveMentionSuggestions =\n client[kInternal].resolveMentionSuggestions !== undefined;\n const self = useSelf();\n const isDisabled = useMemo(\n () => disabled || (self ? !self.canComment : false),\n [disabled, self?.canComment] // eslint-disable-line react-hooks/exhaustive-deps\n );\n const { isEmpty } = useComposer();\n const $ = useOverrides(overrides);\n const [isEmojiPickerOpen, setEmojiPickerOpen] = useState(false);\n const [collapsed, onCollapsedChange] = useControllableState(\n // If the composer is neither controlled nor uncontrolled, it defaults to controlled as uncollapsed.\n controlledCollapsed === undefined && defaultCollapsed === undefined\n ? false\n : controlledCollapsed,\n controlledOnCollapsedChange,\n defaultCollapsed\n );\n\n const preventDefault = useCallback((event: SyntheticEvent) => {\n event.preventDefault();\n }, []);\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n const handleEditorClick = useCallback(\n (event: MouseEvent<HTMLDivElement>) => {\n event.stopPropagation();\n\n if (isEmpty) {\n onCollapsedChange?.(false);\n }\n },\n [isEmpty, onCollapsedChange]\n );\n\n const handleFocus = useCallback(\n (event: FocusEvent<HTMLFormElement>) => {\n onFocus?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n if (isEmpty) {\n onCollapsedChange?.(false);\n }\n },\n [isEmpty, onCollapsedChange, onFocus]\n );\n\n const handleBlur = useCallback(\n (event: FocusEvent<HTMLFormElement>) => {\n onBlur?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n const isOutside = !event.currentTarget.contains(event.relatedTarget);\n\n if (isOutside && isEmpty && !isEmojiPickerOpen) {\n onCollapsedChange?.(true);\n }\n },\n [isEmojiPickerOpen, isEmpty, onBlur, onCollapsedChange]\n );\n\n return (\n <form\n className={classNames(\n \"lb-root lb-composer lb-composer-form\",\n className\n )}\n dir={$.dir}\n {...props}\n ref={forwardedRef}\n data-collapsed={collapsed ? \"\" : undefined}\n onFocus={handleFocus}\n onBlur={handleBlur}\n >\n <ComposerPrimitive.Editor\n className=\"lb-composer-editor\"\n onClick={handleEditorClick}\n placeholder={$.COMPOSER_PLACEHOLDER}\n defaultValue={defaultValue}\n disabled={isDisabled}\n autoFocus={autoFocus}\n components={editorComponents}\n dir={$.dir}\n />\n {!collapsed && (\n <div className=\"lb-composer-footer\">\n <div className=\"lb-composer-editor-actions\">\n {hasResolveMentionSuggestions && (\n <ComposerInsertMentionEditorAction\n label={$.COMPOSER_INSERT_MENTION}\n disabled={isDisabled}\n />\n )}\n <ComposerInsertEmojiEditorAction\n label={$.COMPOSER_INSERT_EMOJI}\n onPickerOpenChange={setEmojiPickerOpen}\n disabled={isDisabled}\n />\n </div>\n {showAttribution && <Attribution />}\n <div className=\"lb-composer-actions\">\n {actions ?? (\n <>\n <ShortcutTooltip\n content={$.COMPOSER_SEND}\n shortcut={<ShortcutTooltipKey name=\"enter\" />}\n >\n <ComposerPrimitive.Submit disabled={isDisabled} asChild>\n <Button\n onMouseDown={preventDefault}\n onClick={stopPropagation}\n className=\"lb-composer-action\"\n variant=\"primary\"\n aria-label={$.COMPOSER_SEND}\n >\n <SendIcon />\n </Button>\n </ComposerPrimitive.Submit>\n </ShortcutTooltip>\n </>\n )}\n </div>\n </div>\n )}\n </form>\n );\n }\n);\n\n/**\n * Displays a composer to create comments.\n *\n * @example\n * <Composer />\n */\nexport const Composer = forwardRef(\n <M extends BaseMetadata = DM>(\n {\n threadId,\n commentId,\n metadata,\n onComposerSubmit,\n onFocus,\n ...props\n }: ComposerProps<M>,\n forwardedRef: ForwardedRef<HTMLFormElement>\n ) => {\n const createThread = useCreateThread();\n const createComment = useCreateComment();\n const editComment = useEditComment();\n\n const handleCommentSubmit = useCallback(\n (comment: ComposerSubmitComment, event: FormEvent<HTMLFormElement>) => {\n onComposerSubmit?.(comment, event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n if (commentId && threadId) {\n editComment({\n commentId,\n threadId,\n body: comment.body,\n });\n } else if (threadId) {\n createComment({\n threadId,\n body: comment.body,\n });\n } else {\n createThread({\n body: comment.body,\n metadata: metadata ?? {},\n });\n }\n },\n [\n commentId,\n createComment,\n createThread,\n editComment,\n metadata,\n onComposerSubmit,\n threadId,\n ]\n );\n\n return (\n <TooltipProvider>\n <ComposerPrimitive.Form\n onComposerSubmit={handleCommentSubmit}\n onFocus={onFocus}\n asChild\n >\n <ComposerWithContext {...props} ref={forwardedRef} />\n </ComposerPrimitive.Form>\n </TooltipProvider>\n );\n }\n) as <M extends BaseMetadata = DM>(\n props: ComposerProps<M> & RefAttributes<HTMLFormElement>\n) => JSX.Element;\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA6JA;AAA2C;AACzC;AACA;AACA;AAEF;AACE;AAEA;AACE;AAAqB;AAGvB;AAAoB;AAEhB;AAEA;AACE;AACA;AAAc;AAChB;AACF;AACuB;AAGzB;AACG;AAAiB;AACf;AAC6D;AAC/C;AACJ;AACG;AACR;AAEH;AAAsB;AAI/B;AAEA;AAAyC;AACvC;AACA;AACA;AAEF;AACE;AAEA;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AACG;AAA2B;AAA0B;AACnD;AAAiB;AACf;AAA0B;AACxB;AAC6D;AAC/C;AACJ;AACG;AACR;AAEH;AAAoB;AAMjC;AAEA;AACE;AACG;AAAoC;AAElC;AAAK;AAGZ;AAEA;AAAoC;AAEpC;AACE;AACG;AAAwC;AACtC;AAA4C;AAExC;AACM;AACK;AACH;AAEN;AACC;AACU;AAEX;AACC;AACU;AAOxB;AAEA;AACE;AACG;AAAuB;AAAsB;AAIlD;AAEA;AAAmD;AACxC;AACW;AAEtB;AAEA;AAA4B;AAKxB;AACE;AACA;AACA;AACW;AACX;AACmB;AACnB;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AAEA;AACA;AAAmB;AAC4B;AAClB;AAE7B;AACA;AACA;AACA;AAAuC;AAIjC;AACJ;AACA;AAGF;AACE;AAAqB;AAGvB;AACE;AAAsB;AAGxB;AAA0B;AAEtB;AAEA;AACE;AAAyB;AAC3B;AACF;AAC2B;AAG7B;AAAoB;AAEhB;AAEA;AACE;AAAA;AAGF;AACE;AAAyB;AAC3B;AACF;AACoC;AAGtC;AAAmB;AAEf;AAEA;AACE;AAAA;AAGF;AAEA;AACE;AAAwB;AAC1B;AACF;AACsD;AAGxD;AACG;AACY;AACT;AACA;AACF;AACO;AACH;AACC;AAC4B;AACxB;AACD;AAEP;AACW;AACD;AACM;AACf;AACU;AACV;AACY;AACL;AAGN;AAAc;AACZ;AAAc;AAEV;AACU;AACC;AAGb;AACU;AACW;AACV;AAIb;AAAc;AAGR;AACY;AACA;AAAwB;AAAQ;AAE1C;AAAmC;AAAmB;AACpD;AACc;AACJ;AACC;AACF;AACM;AAWhC;AAGN;AAQO;AAAiB;AAEpB;AACE;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AAEA;AAA4B;AAExB;AAEA;AACE;AAAA;AAGF;AACE;AAAY;AACV;AACA;AACc;AACf;AAED;AAAc;AACZ;AACc;AACf;AAED;AAAa;AACG;AACS;AACxB;AACH;AACF;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAGF;AAEK;AACmB;AAClB;AACO;AAEN;AAAwB;AAAY;AAEzC;AAGN;;"}