@liveblocks/react-ui 2.7.0-beta1 → 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.
- package/_private/README.md +5 -0
- package/_private/package.json +4 -0
- package/dist/_private/index.d.mts +72 -0
- package/dist/_private/index.d.ts +72 -0
- package/dist/_private/index.js +46 -0
- package/dist/_private/index.js.map +1 -0
- package/dist/_private/index.mjs +21 -0
- package/dist/_private/index.mjs.map +1 -0
- package/dist/components/Comment.js +7 -65
- package/dist/components/Comment.js.map +1 -1
- package/dist/components/Comment.mjs +9 -66
- package/dist/components/Comment.mjs.map +1 -1
- package/dist/components/Composer.js +101 -217
- package/dist/components/Composer.js.map +1 -1
- package/dist/components/Composer.mjs +104 -220
- package/dist/components/Composer.mjs.map +1 -1
- package/dist/components/HistoryVersionSummary.js +42 -0
- package/dist/components/HistoryVersionSummary.js.map +1 -0
- package/dist/components/HistoryVersionSummary.mjs +40 -0
- package/dist/components/HistoryVersionSummary.mjs.map +1 -0
- package/dist/components/HistoryVersionSummaryList.js +22 -0
- package/dist/components/HistoryVersionSummaryList.js.map +1 -0
- package/dist/components/HistoryVersionSummaryList.mjs +20 -0
- package/dist/components/HistoryVersionSummaryList.mjs.map +1 -0
- package/dist/components/InboxNotification.js +10 -25
- package/dist/components/InboxNotification.js.map +1 -1
- package/dist/components/InboxNotification.mjs +10 -25
- package/dist/components/InboxNotification.mjs.map +1 -1
- package/dist/components/Thread.js +0 -2
- package/dist/components/Thread.js.map +1 -1
- package/dist/components/Thread.mjs +0 -2
- package/dist/components/Thread.mjs.map +1 -1
- package/dist/components/internal/Button.js +8 -1
- package/dist/components/internal/Button.js.map +1 -1
- package/dist/components/internal/Button.mjs +8 -1
- package/dist/components/internal/Button.mjs.map +1 -1
- package/dist/components/internal/EmojiPicker.js +3 -3
- package/dist/components/internal/EmojiPicker.js.map +1 -1
- package/dist/components/internal/EmojiPicker.mjs +3 -3
- package/dist/components/internal/EmojiPicker.mjs.map +1 -1
- package/dist/components/internal/InboxNotificationThread.js +2 -10
- package/dist/components/internal/InboxNotificationThread.js.map +1 -1
- package/dist/components/internal/InboxNotificationThread.mjs +3 -11
- package/dist/components/internal/InboxNotificationThread.mjs.map +1 -1
- package/dist/components/internal/User.js +3 -19
- package/dist/components/internal/User.js.map +1 -1
- package/dist/components/internal/User.mjs +3 -19
- package/dist/components/internal/User.mjs.map +1 -1
- package/dist/icons/ArrowUp.js +15 -0
- package/dist/icons/ArrowUp.js.map +1 -0
- package/dist/icons/ArrowUp.mjs +13 -0
- package/dist/icons/ArrowUp.mjs.map +1 -0
- package/dist/icons/{Warning.js → Missing.js} +3 -3
- package/dist/icons/{Warning.js.map → Missing.js.map} +1 -1
- package/dist/icons/{Warning.mjs → Missing.mjs} +3 -3
- package/dist/icons/{Warning.mjs.map → Missing.mjs.map} +1 -1
- package/dist/icons/Restore.js +17 -0
- package/dist/icons/Restore.js.map +1 -0
- package/dist/icons/Restore.mjs +15 -0
- package/dist/icons/Restore.mjs.map +1 -0
- package/dist/icons/Spinner.js +9 -3
- package/dist/icons/Spinner.js.map +1 -1
- package/dist/icons/Spinner.mjs +10 -4
- package/dist/icons/Spinner.mjs.map +1 -1
- package/dist/index.d.mts +74 -70
- package/dist/index.d.ts +74 -70
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3 -0
- package/dist/index.mjs.map +1 -1
- package/dist/overrides.js +5 -6
- package/dist/overrides.js.map +1 -1
- package/dist/overrides.mjs +5 -6
- package/dist/overrides.mjs.map +1 -1
- package/dist/primitives/Composer/contexts.js +3 -17
- package/dist/primitives/Composer/contexts.js.map +1 -1
- package/dist/primitives/Composer/contexts.mjs +4 -15
- package/dist/primitives/Composer/contexts.mjs.map +1 -1
- package/dist/primitives/Composer/index.js +26 -185
- package/dist/primitives/Composer/index.js.map +1 -1
- package/dist/primitives/Composer/index.mjs +31 -188
- package/dist/primitives/Composer/index.mjs.map +1 -1
- package/dist/primitives/Composer/utils.js +0 -224
- package/dist/primitives/Composer/utils.js.map +1 -1
- package/dist/primitives/Composer/utils.mjs +1 -222
- 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/index.d.mts +3 -83
- package/dist/primitives/index.d.ts +3 -83
- package/dist/primitives/index.js +0 -4
- package/dist/primitives/index.js.map +1 -1
- package/dist/primitives/index.mjs +0 -2
- package/dist/primitives/index.mjs.map +1 -1
- package/dist/utils/chunk.js +12 -0
- package/dist/utils/chunk.js.map +1 -0
- package/dist/utils/chunk.mjs +10 -0
- package/dist/utils/chunk.mjs.map +1 -0
- package/dist/utils/intl.js +0 -6
- package/dist/utils/intl.js.map +1 -1
- package/dist/utils/intl.mjs +1 -6
- package/dist/utils/intl.mjs.map +1 -1
- package/dist/utils/use-initial.js +1 -2
- package/dist/utils/use-initial.js.map +1 -1
- package/dist/utils/use-initial.mjs +2 -3
- 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 +16 -4
- package/src/styles/dark/index.css +0 -1
- package/src/styles/index.css +219 -325
- package/src/styles/utils.css +6 -44
- 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/components/internal/Attachment.js +0 -226
- package/dist/components/internal/Attachment.js.map +0 -1
- package/dist/components/internal/Attachment.mjs +0 -224
- package/dist/components/internal/Attachment.mjs.map +0 -1
- package/dist/icons/Attachment.js +0 -15
- package/dist/icons/Attachment.js.map +0 -1
- package/dist/icons/Attachment.mjs +0 -13
- package/dist/icons/Attachment.mjs.map +0 -1
- package/dist/primitives/FileSize.js +0 -33
- package/dist/primitives/FileSize.js.map +0 -1
- package/dist/primitives/FileSize.mjs +0 -31
- package/dist/primitives/FileSize.mjs.map +0 -1
- package/dist/utils/download.js +0 -14
- package/dist/utils/download.js.map +0 -1
- package/dist/utils/download.mjs +0 -12
- package/dist/utils/download.mjs.map +0 -1
- package/dist/utils/format-file-size.js +0 -45
- package/dist/utils/format-file-size.js.map +0 -1
- package/dist/utils/format-file-size.mjs +0 -43
- 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
|
-
|
|
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
|
-
|
|
281
|
-
|
|
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
|
|
302
|
-
const
|
|
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 [
|
|
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
|
|
310
|
-
|
|
155
|
+
const preventDefault = React.useCallback((event) => {
|
|
156
|
+
event.preventDefault();
|
|
311
157
|
}, []);
|
|
312
|
-
const
|
|
313
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
391
|
-
|
|
392
|
-
|
|
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;;"}
|