@liveblocks/react-ui 2.5.2 → 2.7.0-beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Comment.js +61 -3
- package/dist/components/Comment.js.map +1 -1
- package/dist/components/Comment.mjs +62 -5
- package/dist/components/Comment.mjs.map +1 -1
- package/dist/components/Composer.js +217 -101
- package/dist/components/Composer.js.map +1 -1
- package/dist/components/Composer.mjs +220 -104
- package/dist/components/Composer.mjs.map +1 -1
- package/dist/components/InboxNotification.js +17 -4
- package/dist/components/InboxNotification.js.map +1 -1
- package/dist/components/InboxNotification.mjs +17 -4
- package/dist/components/InboxNotification.mjs.map +1 -1
- package/dist/components/Thread.js +2 -0
- package/dist/components/Thread.js.map +1 -1
- package/dist/components/Thread.mjs +2 -0
- package/dist/components/Thread.mjs.map +1 -1
- package/dist/components/internal/Attachment.js +226 -0
- package/dist/components/internal/Attachment.js.map +1 -0
- package/dist/components/internal/Attachment.mjs +224 -0
- package/dist/components/internal/Attachment.mjs.map +1 -0
- package/dist/components/internal/InboxNotificationThread.js +10 -2
- package/dist/components/internal/InboxNotificationThread.js.map +1 -1
- package/dist/components/internal/InboxNotificationThread.mjs +11 -3
- package/dist/components/internal/InboxNotificationThread.mjs.map +1 -1
- package/dist/icons/Attachment.js +15 -0
- package/dist/icons/Attachment.js.map +1 -0
- package/dist/icons/Attachment.mjs +13 -0
- package/dist/icons/Attachment.mjs.map +1 -0
- package/dist/icons/Spinner.js +3 -9
- package/dist/icons/Spinner.js.map +1 -1
- package/dist/icons/Spinner.mjs +4 -10
- package/dist/icons/Spinner.mjs.map +1 -1
- package/dist/icons/{Missing.js → Warning.js} +3 -3
- package/dist/icons/{Missing.js.map → Warning.js.map} +1 -1
- package/dist/icons/{Missing.mjs → Warning.mjs} +3 -3
- package/dist/icons/{Missing.mjs.map → Warning.mjs.map} +1 -1
- package/dist/index.d.mts +70 -4
- package/dist/index.d.ts +70 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/overrides.js +5 -0
- package/dist/overrides.js.map +1 -1
- package/dist/overrides.mjs +5 -0
- package/dist/overrides.mjs.map +1 -1
- package/dist/primitives/Composer/contexts.js +17 -3
- package/dist/primitives/Composer/contexts.js.map +1 -1
- package/dist/primitives/Composer/contexts.mjs +15 -4
- package/dist/primitives/Composer/contexts.mjs.map +1 -1
- package/dist/primitives/Composer/index.js +185 -26
- package/dist/primitives/Composer/index.js.map +1 -1
- package/dist/primitives/Composer/index.mjs +188 -31
- package/dist/primitives/Composer/index.mjs.map +1 -1
- package/dist/primitives/Composer/utils.js +224 -0
- package/dist/primitives/Composer/utils.js.map +1 -1
- package/dist/primitives/Composer/utils.mjs +222 -1
- package/dist/primitives/Composer/utils.mjs.map +1 -1
- package/dist/primitives/EmojiPicker/utils.js +2 -2
- package/dist/primitives/EmojiPicker/utils.js.map +1 -1
- package/dist/primitives/EmojiPicker/utils.mjs +1 -1
- package/dist/primitives/EmojiPicker/utils.mjs.map +1 -1
- package/dist/primitives/FileSize.js +33 -0
- package/dist/primitives/FileSize.js.map +1 -0
- package/dist/primitives/FileSize.mjs +31 -0
- package/dist/primitives/FileSize.mjs.map +1 -0
- package/dist/primitives/index.d.mts +83 -3
- package/dist/primitives/index.d.ts +83 -3
- package/dist/primitives/index.js +4 -0
- package/dist/primitives/index.js.map +1 -1
- package/dist/primitives/index.mjs +2 -0
- package/dist/primitives/index.mjs.map +1 -1
- package/dist/utils/download.js +14 -0
- package/dist/utils/download.js.map +1 -0
- package/dist/utils/download.mjs +12 -0
- package/dist/utils/download.mjs.map +1 -0
- package/dist/utils/format-file-size.js +45 -0
- package/dist/utils/format-file-size.js.map +1 -0
- package/dist/utils/format-file-size.mjs +43 -0
- package/dist/utils/format-file-size.mjs.map +1 -0
- package/dist/utils/intl.js +6 -0
- package/dist/utils/intl.js.map +1 -1
- package/dist/utils/intl.mjs +6 -1
- package/dist/utils/intl.mjs.map +1 -1
- package/dist/utils/use-initial.js +2 -1
- package/dist/utils/use-initial.js.map +1 -1
- package/dist/utils/use-initial.mjs +3 -2
- package/dist/utils/use-initial.mjs.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/dist/version.mjs +1 -1
- package/dist/version.mjs.map +1 -1
- package/package.json +4 -4
- package/src/styles/dark/index.css +1 -0
- package/src/styles/index.css +296 -62
- package/src/styles/utils.css +44 -0
- package/styles/dark/attributes.css +1 -1
- package/styles/dark/attributes.css.map +1 -1
- package/styles/dark/media-query.css +1 -1
- package/styles/dark/media-query.css.map +1 -1
- package/styles.css +1 -1
- package/styles.css.map +1 -1
- package/dist/utils/chunk.js +0 -12
- package/dist/utils/chunk.js.map +0 -1
- package/dist/utils/chunk.mjs +0 -10
- package/dist/utils/chunk.mjs.map +0 -1
|
@@ -4,15 +4,19 @@
|
|
|
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');
|
|
7
8
|
var Emoji = require('../icons/Emoji.js');
|
|
8
9
|
var Mention = require('../icons/Mention.js');
|
|
9
10
|
var Send = require('../icons/Send.js');
|
|
10
11
|
var overrides = require('../overrides.js');
|
|
11
12
|
var index = require('../primitives/Composer/index.js');
|
|
12
13
|
var contexts = require('../primitives/Composer/contexts.js');
|
|
14
|
+
var utils = require('../primitives/Composer/utils.js');
|
|
13
15
|
var mentions = require('../slate/plugins/mentions.js');
|
|
14
16
|
var classNames = require('../utils/class-names.js');
|
|
15
17
|
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');
|
|
16
20
|
var Attribution = require('./internal/Attribution.js');
|
|
17
21
|
var Avatar = require('./internal/Avatar.js');
|
|
18
22
|
var Button = require('./internal/Button.js');
|
|
@@ -84,6 +88,31 @@ function ComposerInsertEmojiEditorAction({
|
|
|
84
88
|
className: "lb-button-icon"
|
|
85
89
|
})))));
|
|
86
90
|
}
|
|
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
|
+
}
|
|
87
116
|
function ComposerMention({ userId }) {
|
|
88
117
|
return /* @__PURE__ */ React.createElement(index.Mention, {
|
|
89
118
|
className: "lb-composer-mention"
|
|
@@ -116,68 +145,184 @@ function ComposerLink({ href, children }) {
|
|
|
116
145
|
className: "lb-composer-link"
|
|
117
146
|
}, children);
|
|
118
147
|
}
|
|
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
|
+
}
|
|
119
187
|
const editorComponents = {
|
|
120
188
|
Mention: ComposerMention,
|
|
121
189
|
MentionSuggestions: ComposerMentionSuggestions,
|
|
122
190
|
Link: ComposerLink
|
|
123
191
|
};
|
|
124
|
-
|
|
125
|
-
|
|
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,
|
|
126
236
|
defaultValue,
|
|
127
|
-
disabled,
|
|
128
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(
|
|
275
|
+
({
|
|
276
|
+
threadId,
|
|
277
|
+
commentId,
|
|
278
|
+
metadata,
|
|
279
|
+
defaultValue,
|
|
280
|
+
defaultAttachments,
|
|
281
|
+
onComposerSubmit,
|
|
129
282
|
collapsed: controlledCollapsed,
|
|
130
283
|
defaultCollapsed,
|
|
131
284
|
onCollapsedChange: controlledOnCollapsedChange,
|
|
132
|
-
actions,
|
|
133
285
|
overrides: overrides$1,
|
|
134
|
-
|
|
135
|
-
onFocus,
|
|
286
|
+
actions,
|
|
136
287
|
onBlur,
|
|
137
288
|
className,
|
|
289
|
+
onFocus,
|
|
290
|
+
autoFocus,
|
|
291
|
+
disabled,
|
|
292
|
+
showAttachments = true,
|
|
293
|
+
showAttribution,
|
|
138
294
|
...props
|
|
139
295
|
}, forwardedRef) => {
|
|
140
296
|
const client = react.useClient();
|
|
297
|
+
const createThread = react.useCreateThread();
|
|
298
|
+
const createComment = react.useCreateComment();
|
|
299
|
+
const editComment = react.useEditComment();
|
|
141
300
|
const hasResolveMentionSuggestions = client[core.kInternal].resolveMentionSuggestions !== void 0;
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
() => disabled || (self ? !self.canComment : false),
|
|
145
|
-
[disabled, self?.canComment]
|
|
146
|
-
);
|
|
147
|
-
const { isEmpty } = contexts.useComposer();
|
|
301
|
+
const isEmptyRef = React.useRef(true);
|
|
302
|
+
const isEmojiPickerOpenRef = React.useRef(false);
|
|
148
303
|
const $ = overrides.useOverrides(overrides$1);
|
|
149
|
-
const [
|
|
150
|
-
const [collapsed, onCollapsedChange] = useControllableState.useControllableState(
|
|
304
|
+
const [isCollapsed, onCollapsedChange] = useControllableState.useControllableState(
|
|
151
305
|
controlledCollapsed === void 0 && defaultCollapsed === void 0 ? false : controlledCollapsed,
|
|
152
306
|
controlledOnCollapsedChange,
|
|
153
307
|
defaultCollapsed
|
|
154
308
|
);
|
|
155
|
-
const
|
|
156
|
-
|
|
309
|
+
const setEmptyRef = React.useCallback((isEmpty) => {
|
|
310
|
+
isEmptyRef.current = isEmpty;
|
|
157
311
|
}, []);
|
|
158
|
-
const
|
|
159
|
-
|
|
312
|
+
const setEmojiPickerOpenRef = React.useCallback((isEmojiPickerOpen) => {
|
|
313
|
+
isEmojiPickerOpenRef.current = isEmojiPickerOpen;
|
|
160
314
|
}, []);
|
|
161
|
-
const handleEditorClick = React.useCallback(
|
|
162
|
-
(event) => {
|
|
163
|
-
event.stopPropagation();
|
|
164
|
-
if (isEmpty) {
|
|
165
|
-
onCollapsedChange?.(false);
|
|
166
|
-
}
|
|
167
|
-
},
|
|
168
|
-
[isEmpty, onCollapsedChange]
|
|
169
|
-
);
|
|
170
315
|
const handleFocus = React.useCallback(
|
|
171
316
|
(event) => {
|
|
172
317
|
onFocus?.(event);
|
|
173
318
|
if (event.isDefaultPrevented()) {
|
|
174
319
|
return;
|
|
175
320
|
}
|
|
176
|
-
if (
|
|
321
|
+
if (isEmptyRef.current) {
|
|
177
322
|
onCollapsedChange?.(false);
|
|
178
323
|
}
|
|
179
324
|
},
|
|
180
|
-
[
|
|
325
|
+
[onCollapsedChange, onFocus]
|
|
181
326
|
);
|
|
182
327
|
const handleBlur = React.useCallback(
|
|
183
328
|
(event) => {
|
|
@@ -185,75 +330,24 @@ const ComposerWithContext = React.forwardRef(
|
|
|
185
330
|
if (event.isDefaultPrevented()) {
|
|
186
331
|
return;
|
|
187
332
|
}
|
|
188
|
-
const isOutside = !event.currentTarget.contains(
|
|
189
|
-
|
|
333
|
+
const isOutside = !event.currentTarget.contains(
|
|
334
|
+
event.relatedTarget ?? document.activeElement
|
|
335
|
+
);
|
|
336
|
+
if (isOutside && isEmptyRef.current && !isEmojiPickerOpenRef.current) {
|
|
190
337
|
onCollapsedChange?.(true);
|
|
191
338
|
}
|
|
192
339
|
},
|
|
193
|
-
[
|
|
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]
|
|
194
350
|
);
|
|
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();
|
|
257
351
|
const handleCommentSubmit = React.useCallback(
|
|
258
352
|
(comment, event) => {
|
|
259
353
|
onComposerSubmit?.(comment, event);
|
|
@@ -264,17 +358,20 @@ const Composer = React.forwardRef(
|
|
|
264
358
|
editComment({
|
|
265
359
|
commentId,
|
|
266
360
|
threadId,
|
|
267
|
-
body: comment.body
|
|
361
|
+
body: comment.body,
|
|
362
|
+
attachments: comment.attachments
|
|
268
363
|
});
|
|
269
364
|
} else if (threadId) {
|
|
270
365
|
createComment({
|
|
271
366
|
threadId,
|
|
272
|
-
body: comment.body
|
|
367
|
+
body: comment.body,
|
|
368
|
+
attachments: comment.attachments
|
|
273
369
|
});
|
|
274
370
|
} else {
|
|
275
371
|
createThread({
|
|
276
372
|
body: comment.body,
|
|
277
|
-
metadata: metadata ?? {}
|
|
373
|
+
metadata: metadata ?? {},
|
|
374
|
+
attachments: comment.attachments
|
|
278
375
|
});
|
|
279
376
|
}
|
|
280
377
|
},
|
|
@@ -290,11 +387,30 @@ const Composer = React.forwardRef(
|
|
|
290
387
|
);
|
|
291
388
|
return /* @__PURE__ */ React.createElement(TooltipPrimitive.TooltipProvider, null, /* @__PURE__ */ React.createElement(index.Form, {
|
|
292
389
|
onComposerSubmit: handleCommentSubmit,
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
390
|
+
className: classNames.classNames(
|
|
391
|
+
"lb-root lb-composer lb-composer-form",
|
|
392
|
+
className
|
|
393
|
+
),
|
|
394
|
+
dir: $.dir,
|
|
296
395
|
...props,
|
|
297
|
-
ref: forwardedRef
|
|
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
|
|
298
414
|
})));
|
|
299
415
|
}
|
|
300
416
|
);
|
|
@@ -1 +1 @@
|
|
|
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;;"}
|
|
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;;"}
|