@liveblocks/react-ui 2.9.3-emails1 → 2.10.0
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/index.d.mts +1 -9
- package/dist/index.d.ts +1 -9
- package/dist/index.js +0 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +0 -1
- package/dist/index.mjs.map +1 -1
- package/dist/primitives/Composer/index.js +38 -22
- package/dist/primitives/Composer/index.js.map +1 -1
- package/dist/primitives/Composer/index.mjs +37 -21
- package/dist/primitives/Composer/index.mjs.map +1 -1
- package/dist/shared.js +0 -61
- package/dist/shared.js.map +1 -1
- package/dist/shared.mjs +4 -64
- package/dist/shared.mjs.map +1 -1
- package/dist/slate/plugins/normalize.js +28 -0
- package/dist/slate/plugins/normalize.js.map +1 -0
- package/dist/slate/plugins/normalize.mjs +26 -0
- package/dist/slate/plugins/normalize.mjs.map +1 -0
- package/dist/slate/plugins/paste.js +44 -8
- package/dist/slate/plugins/paste.js.map +1 -1
- package/dist/slate/plugins/paste.mjs +44 -8
- package/dist/slate/plugins/paste.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/dist/index.d.mts
CHANGED
|
@@ -646,12 +646,4 @@ interface TimestampProps extends Omit<ComponentPropsWithSlot<"time">, "children"
|
|
|
646
646
|
*/
|
|
647
647
|
declare const Timestamp: React.ForwardRefExoticComponent<TimestampProps & React.RefAttributes<HTMLTimeElement>>;
|
|
648
648
|
|
|
649
|
-
|
|
650
|
-
* @private For internal use only. Do not rely on this hook.
|
|
651
|
-
*
|
|
652
|
-
* Simplistic debounced search, we don't need to worry too much about deduping
|
|
653
|
-
* and race conditions as there can only be one search at a time.
|
|
654
|
-
*/
|
|
655
|
-
declare function useMentionSuggestions(search?: string): string[] | undefined;
|
|
656
|
-
|
|
657
|
-
export { Comment, CommentAttachmentArgs, CommentOverrides, CommentProps, Composer, ComposerOverrides, ComposerProps, ComposerSubmitComment, GlobalOverrides, HistoryVersionSummary, HistoryVersionSummaryList, HistoryVersionSummaryListProps, HistoryVersionSummaryProps, InboxNotification, InboxNotificationAvatarProps, InboxNotificationCustomKindProps, InboxNotificationCustomProps, InboxNotificationIconProps, InboxNotificationList, InboxNotificationListProps, InboxNotificationOverrides, InboxNotificationProps, InboxNotificationTextMentionKindProps, InboxNotificationTextMentionProps, InboxNotificationThreadKindProps, InboxNotificationThreadProps, LiveblocksUIConfig, LocalizationOverrides, Overrides, Thread, ThreadOverrides, ThreadProps, Timestamp, useMentionSuggestions, useOverrides };
|
|
649
|
+
export { Comment, CommentAttachmentArgs, CommentOverrides, CommentProps, Composer, ComposerOverrides, ComposerProps, ComposerSubmitComment, GlobalOverrides, HistoryVersionSummary, HistoryVersionSummaryList, HistoryVersionSummaryListProps, HistoryVersionSummaryProps, InboxNotification, InboxNotificationAvatarProps, InboxNotificationCustomKindProps, InboxNotificationCustomProps, InboxNotificationIconProps, InboxNotificationList, InboxNotificationListProps, InboxNotificationOverrides, InboxNotificationProps, InboxNotificationTextMentionKindProps, InboxNotificationTextMentionProps, InboxNotificationThreadKindProps, InboxNotificationThreadProps, LiveblocksUIConfig, LocalizationOverrides, Overrides, Thread, ThreadOverrides, ThreadProps, Timestamp, useOverrides };
|
package/dist/index.d.ts
CHANGED
|
@@ -646,12 +646,4 @@ interface TimestampProps extends Omit<ComponentPropsWithSlot<"time">, "children"
|
|
|
646
646
|
*/
|
|
647
647
|
declare const Timestamp: React.ForwardRefExoticComponent<TimestampProps & React.RefAttributes<HTMLTimeElement>>;
|
|
648
648
|
|
|
649
|
-
|
|
650
|
-
* @private For internal use only. Do not rely on this hook.
|
|
651
|
-
*
|
|
652
|
-
* Simplistic debounced search, we don't need to worry too much about deduping
|
|
653
|
-
* and race conditions as there can only be one search at a time.
|
|
654
|
-
*/
|
|
655
|
-
declare function useMentionSuggestions(search?: string): string[] | undefined;
|
|
656
|
-
|
|
657
|
-
export { Comment, CommentAttachmentArgs, CommentOverrides, CommentProps, Composer, ComposerOverrides, ComposerProps, ComposerSubmitComment, GlobalOverrides, HistoryVersionSummary, HistoryVersionSummaryList, HistoryVersionSummaryListProps, HistoryVersionSummaryProps, InboxNotification, InboxNotificationAvatarProps, InboxNotificationCustomKindProps, InboxNotificationCustomProps, InboxNotificationIconProps, InboxNotificationList, InboxNotificationListProps, InboxNotificationOverrides, InboxNotificationProps, InboxNotificationTextMentionKindProps, InboxNotificationTextMentionProps, InboxNotificationThreadKindProps, InboxNotificationThreadProps, LiveblocksUIConfig, LocalizationOverrides, Overrides, Thread, ThreadOverrides, ThreadProps, Timestamp, useMentionSuggestions, useOverrides };
|
|
649
|
+
export { Comment, CommentAttachmentArgs, CommentOverrides, CommentProps, Composer, ComposerOverrides, ComposerProps, ComposerSubmitComment, GlobalOverrides, HistoryVersionSummary, HistoryVersionSummaryList, HistoryVersionSummaryListProps, HistoryVersionSummaryProps, InboxNotification, InboxNotificationAvatarProps, InboxNotificationCustomKindProps, InboxNotificationCustomProps, InboxNotificationIconProps, InboxNotificationList, InboxNotificationListProps, InboxNotificationOverrides, InboxNotificationProps, InboxNotificationTextMentionKindProps, InboxNotificationTextMentionProps, InboxNotificationThreadKindProps, InboxNotificationThreadProps, LiveblocksUIConfig, LocalizationOverrides, Overrides, Thread, ThreadOverrides, ThreadProps, Timestamp, useOverrides };
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,6 @@ var Thread = require('./components/Thread.js');
|
|
|
12
12
|
var config = require('./config.js');
|
|
13
13
|
var overrides = require('./overrides.js');
|
|
14
14
|
var Timestamp = require('./primitives/Timestamp.js');
|
|
15
|
-
var shared = require('./shared.js');
|
|
16
15
|
|
|
17
16
|
core.detectDupes(version.PKG_NAME, version.PKG_VERSION, version.PKG_FORMAT);
|
|
18
17
|
|
|
@@ -26,5 +25,4 @@ exports.Thread = Thread.Thread;
|
|
|
26
25
|
exports.LiveblocksUIConfig = config.LiveblocksUIConfig;
|
|
27
26
|
exports.useOverrides = overrides.useOverrides;
|
|
28
27
|
exports.Timestamp = Timestamp.Timestamp;
|
|
29
|
-
exports.useMentionSuggestions = shared.useMentionSuggestions;
|
|
30
28
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import { detectDupes } from \"@liveblocks/core\";\n\nimport { PKG_FORMAT, PKG_NAME, PKG_VERSION } from \"./version\";\n\ndetectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);\n\nexport type { CommentProps } from \"./components/Comment\";\nexport { Comment } from \"./components/Comment\";\nexport type { ComposerProps } from \"./components/Composer\";\nexport { Composer } from \"./components/Composer\";\nexport type { HistoryVersionSummaryProps } from \"./components/HistoryVersionSummary\";\nexport { HistoryVersionSummary } from \"./components/HistoryVersionSummary\";\nexport type { HistoryVersionSummaryListProps } from \"./components/HistoryVersionSummaryList\";\nexport { HistoryVersionSummaryList } from \"./components/HistoryVersionSummaryList\";\nexport type {\n InboxNotificationAvatarProps,\n InboxNotificationCustomKindProps,\n InboxNotificationCustomProps,\n InboxNotificationIconProps,\n InboxNotificationProps,\n InboxNotificationTextMentionKindProps,\n InboxNotificationTextMentionProps,\n InboxNotificationThreadKindProps,\n InboxNotificationThreadProps,\n} from \"./components/InboxNotification\";\nexport { InboxNotification } from \"./components/InboxNotification\";\nexport type { InboxNotificationListProps } from \"./components/InboxNotificationList\";\nexport { InboxNotificationList } from \"./components/InboxNotificationList\";\nexport type { ThreadProps } from \"./components/Thread\";\nexport { Thread } from \"./components/Thread\";\nexport { LiveblocksUIConfig } from \"./config\";\nexport type {\n CommentOverrides,\n ComposerOverrides,\n GlobalOverrides,\n InboxNotificationOverrides,\n LocalizationOverrides,\n Overrides,\n ThreadOverrides,\n} from \"./overrides\";\nexport { useOverrides } from \"./overrides\";\nexport type { ComposerSubmitComment } from \"./primitives\";\nexport { Timestamp } from \"./primitives/Timestamp\";\nexport
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import { detectDupes } from \"@liveblocks/core\";\n\nimport { PKG_FORMAT, PKG_NAME, PKG_VERSION } from \"./version\";\n\ndetectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);\n\nexport type { CommentProps } from \"./components/Comment\";\nexport { Comment } from \"./components/Comment\";\nexport type { ComposerProps } from \"./components/Composer\";\nexport { Composer } from \"./components/Composer\";\nexport type { HistoryVersionSummaryProps } from \"./components/HistoryVersionSummary\";\nexport { HistoryVersionSummary } from \"./components/HistoryVersionSummary\";\nexport type { HistoryVersionSummaryListProps } from \"./components/HistoryVersionSummaryList\";\nexport { HistoryVersionSummaryList } from \"./components/HistoryVersionSummaryList\";\nexport type {\n InboxNotificationAvatarProps,\n InboxNotificationCustomKindProps,\n InboxNotificationCustomProps,\n InboxNotificationIconProps,\n InboxNotificationProps,\n InboxNotificationTextMentionKindProps,\n InboxNotificationTextMentionProps,\n InboxNotificationThreadKindProps,\n InboxNotificationThreadProps,\n} from \"./components/InboxNotification\";\nexport { InboxNotification } from \"./components/InboxNotification\";\nexport type { InboxNotificationListProps } from \"./components/InboxNotificationList\";\nexport { InboxNotificationList } from \"./components/InboxNotificationList\";\nexport type { ThreadProps } from \"./components/Thread\";\nexport { Thread } from \"./components/Thread\";\nexport { LiveblocksUIConfig } from \"./config\";\nexport type {\n CommentOverrides,\n ComposerOverrides,\n GlobalOverrides,\n InboxNotificationOverrides,\n LocalizationOverrides,\n Overrides,\n ThreadOverrides,\n} from \"./overrides\";\nexport { useOverrides } from \"./overrides\";\nexport type { ComposerSubmitComment } from \"./primitives\";\nexport { Timestamp } from \"./primitives/Timestamp\";\nexport type { CommentAttachmentArgs } from \"./types\";\n"],"names":["detectDupes","PKG_NAME","PKG_VERSION","PKG_FORMAT"],"mappings":";;;;;;;;;;;;;;;AAIAA,gBAAY,CAAAC,gBAAA,EAAUC,qBAAaC,kBAAU,CAAA;;;;;;;;;;;;;"}
|
package/dist/index.mjs
CHANGED
|
@@ -10,7 +10,6 @@ export { Thread } from './components/Thread.mjs';
|
|
|
10
10
|
export { LiveblocksUIConfig } from './config.mjs';
|
|
11
11
|
export { useOverrides } from './overrides.mjs';
|
|
12
12
|
export { Timestamp } from './primitives/Timestamp.mjs';
|
|
13
|
-
export { useMentionSuggestions } from './shared.mjs';
|
|
14
13
|
|
|
15
14
|
detectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);
|
|
16
15
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../src/index.ts"],"sourcesContent":["import { detectDupes } from \"@liveblocks/core\";\n\nimport { PKG_FORMAT, PKG_NAME, PKG_VERSION } from \"./version\";\n\ndetectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);\n\nexport type { CommentProps } from \"./components/Comment\";\nexport { Comment } from \"./components/Comment\";\nexport type { ComposerProps } from \"./components/Composer\";\nexport { Composer } from \"./components/Composer\";\nexport type { HistoryVersionSummaryProps } from \"./components/HistoryVersionSummary\";\nexport { HistoryVersionSummary } from \"./components/HistoryVersionSummary\";\nexport type { HistoryVersionSummaryListProps } from \"./components/HistoryVersionSummaryList\";\nexport { HistoryVersionSummaryList } from \"./components/HistoryVersionSummaryList\";\nexport type {\n InboxNotificationAvatarProps,\n InboxNotificationCustomKindProps,\n InboxNotificationCustomProps,\n InboxNotificationIconProps,\n InboxNotificationProps,\n InboxNotificationTextMentionKindProps,\n InboxNotificationTextMentionProps,\n InboxNotificationThreadKindProps,\n InboxNotificationThreadProps,\n} from \"./components/InboxNotification\";\nexport { InboxNotification } from \"./components/InboxNotification\";\nexport type { InboxNotificationListProps } from \"./components/InboxNotificationList\";\nexport { InboxNotificationList } from \"./components/InboxNotificationList\";\nexport type { ThreadProps } from \"./components/Thread\";\nexport { Thread } from \"./components/Thread\";\nexport { LiveblocksUIConfig } from \"./config\";\nexport type {\n CommentOverrides,\n ComposerOverrides,\n GlobalOverrides,\n InboxNotificationOverrides,\n LocalizationOverrides,\n Overrides,\n ThreadOverrides,\n} from \"./overrides\";\nexport { useOverrides } from \"./overrides\";\nexport type { ComposerSubmitComment } from \"./primitives\";\nexport { Timestamp } from \"./primitives/Timestamp\";\nexport
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/index.ts"],"sourcesContent":["import { detectDupes } from \"@liveblocks/core\";\n\nimport { PKG_FORMAT, PKG_NAME, PKG_VERSION } from \"./version\";\n\ndetectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);\n\nexport type { CommentProps } from \"./components/Comment\";\nexport { Comment } from \"./components/Comment\";\nexport type { ComposerProps } from \"./components/Composer\";\nexport { Composer } from \"./components/Composer\";\nexport type { HistoryVersionSummaryProps } from \"./components/HistoryVersionSummary\";\nexport { HistoryVersionSummary } from \"./components/HistoryVersionSummary\";\nexport type { HistoryVersionSummaryListProps } from \"./components/HistoryVersionSummaryList\";\nexport { HistoryVersionSummaryList } from \"./components/HistoryVersionSummaryList\";\nexport type {\n InboxNotificationAvatarProps,\n InboxNotificationCustomKindProps,\n InboxNotificationCustomProps,\n InboxNotificationIconProps,\n InboxNotificationProps,\n InboxNotificationTextMentionKindProps,\n InboxNotificationTextMentionProps,\n InboxNotificationThreadKindProps,\n InboxNotificationThreadProps,\n} from \"./components/InboxNotification\";\nexport { InboxNotification } from \"./components/InboxNotification\";\nexport type { InboxNotificationListProps } from \"./components/InboxNotificationList\";\nexport { InboxNotificationList } from \"./components/InboxNotificationList\";\nexport type { ThreadProps } from \"./components/Thread\";\nexport { Thread } from \"./components/Thread\";\nexport { LiveblocksUIConfig } from \"./config\";\nexport type {\n CommentOverrides,\n ComposerOverrides,\n GlobalOverrides,\n InboxNotificationOverrides,\n LocalizationOverrides,\n Overrides,\n ThreadOverrides,\n} from \"./overrides\";\nexport { useOverrides } from \"./overrides\";\nexport type { ComposerSubmitComment } from \"./primitives\";\nexport { Timestamp } from \"./primitives/Timestamp\";\nexport type { CommentAttachmentArgs } from \"./types\";\n"],"names":[],"mappings":";;;;;;;;;;;;;AAIA,WAAY,CAAA,QAAA,EAAU,aAAa,UAAU,CAAA"}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
var reactDom = require('@floating-ui/react-dom');
|
|
5
5
|
var react = require('@liveblocks/react');
|
|
6
|
+
var _private = require('@liveblocks/react/_private');
|
|
6
7
|
var reactSlot = require('@radix-ui/react-slot');
|
|
7
8
|
var React = require('react');
|
|
8
9
|
var slate = require('slate');
|
|
@@ -10,12 +11,12 @@ var slateHistory = require('slate-history');
|
|
|
10
11
|
var slateReact = require('slate-react');
|
|
11
12
|
var config = require('../../config.js');
|
|
12
13
|
var constants = require('../../constants.js');
|
|
13
|
-
var shared = require('../../shared.js');
|
|
14
14
|
var autoFormatting = require('../../slate/plugins/auto-formatting.js');
|
|
15
15
|
var autoLinks = require('../../slate/plugins/auto-links.js');
|
|
16
16
|
var customLinks = require('../../slate/plugins/custom-links.js');
|
|
17
17
|
var emptyClearFormatting = require('../../slate/plugins/empty-clear-formatting.js');
|
|
18
18
|
var mentions = require('../../slate/plugins/mentions.js');
|
|
19
|
+
var normalize = require('../../slate/plugins/normalize.js');
|
|
19
20
|
var paste = require('../../slate/plugins/paste.js');
|
|
20
21
|
var getDomRange = require('../../slate/utils/get-dom-range.js');
|
|
21
22
|
var isEmpty = require('../../slate/utils/is-empty.js');
|
|
@@ -52,15 +53,17 @@ function createComposerEditor({
|
|
|
52
53
|
createAttachments,
|
|
53
54
|
pasteFilesAsAttachments
|
|
54
55
|
}) {
|
|
55
|
-
return
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
return normalize.withNormalize(
|
|
57
|
+
mentions.withMentions(
|
|
58
|
+
customLinks.withCustomLinks(
|
|
59
|
+
autoLinks.withAutoLinks(
|
|
60
|
+
autoFormatting.withAutoFormatting(
|
|
61
|
+
emptyClearFormatting.withEmptyClearFormatting(
|
|
62
|
+
paste.withPaste(slateHistory.withHistory(slateReact.withReact(slate.createEditor())), {
|
|
63
|
+
createAttachments,
|
|
64
|
+
pasteFilesAsAttachments
|
|
65
|
+
})
|
|
66
|
+
)
|
|
64
67
|
)
|
|
65
68
|
)
|
|
66
69
|
)
|
|
@@ -322,7 +325,15 @@ const ComposerSuggestionsList = React.forwardRef(({ children, asChild, ...props
|
|
|
322
325
|
}, children);
|
|
323
326
|
});
|
|
324
327
|
const ComposerSuggestionsListItem = React.forwardRef(
|
|
325
|
-
({
|
|
328
|
+
({
|
|
329
|
+
value,
|
|
330
|
+
children,
|
|
331
|
+
onPointerMove,
|
|
332
|
+
onPointerDown,
|
|
333
|
+
onClick,
|
|
334
|
+
asChild,
|
|
335
|
+
...props
|
|
336
|
+
}, forwardedRef) => {
|
|
326
337
|
const ref = React.useRef(null);
|
|
327
338
|
const mergedRefs = useRefs.useRefs(forwardedRef, ref);
|
|
328
339
|
const { selectedValue, setSelectedValue, itemId, onItemSelect } = contexts.useComposerSuggestionsContext(COMPOSER_SUGGESTIONS_LIST_ITEM_NAME);
|
|
@@ -349,18 +360,22 @@ const ComposerSuggestionsListItem = React.forwardRef(
|
|
|
349
360
|
const handlePointerDown = React.useCallback(
|
|
350
361
|
(event) => {
|
|
351
362
|
onPointerDown?.(event);
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
363
|
+
event.preventDefault();
|
|
364
|
+
event.stopPropagation();
|
|
365
|
+
},
|
|
366
|
+
[onPointerDown]
|
|
367
|
+
);
|
|
368
|
+
const handleClick = React.useCallback(
|
|
369
|
+
(event) => {
|
|
370
|
+
onClick?.(event);
|
|
371
|
+
const wasDefaultPrevented = event.isDefaultPrevented();
|
|
372
|
+
event.preventDefault();
|
|
373
|
+
event.stopPropagation();
|
|
374
|
+
if (!wasDefaultPrevented) {
|
|
375
|
+
onItemSelect(value);
|
|
361
376
|
}
|
|
362
377
|
},
|
|
363
|
-
[
|
|
378
|
+
[onClick, onItemSelect, value]
|
|
364
379
|
);
|
|
365
380
|
return /* @__PURE__ */ React.createElement(Component, {
|
|
366
381
|
role: "option",
|
|
@@ -369,6 +384,7 @@ const ComposerSuggestionsListItem = React.forwardRef(
|
|
|
369
384
|
"aria-selected": isSelected || void 0,
|
|
370
385
|
onPointerMove: handlePointerMove,
|
|
371
386
|
onPointerDown: handlePointerDown,
|
|
387
|
+
onClick: handleClick,
|
|
372
388
|
...props,
|
|
373
389
|
ref: mergedRefs
|
|
374
390
|
}, children);
|
|
@@ -421,7 +437,7 @@ const ComposerEditor = React.forwardRef(
|
|
|
421
437
|
[components]
|
|
422
438
|
);
|
|
423
439
|
const [mentionDraft, setMentionDraft] = React.useState();
|
|
424
|
-
const mentionSuggestions =
|
|
440
|
+
const mentionSuggestions = _private.useMentionSuggestions(mentionDraft?.text);
|
|
425
441
|
const [
|
|
426
442
|
selectedMentionSuggestionIndex,
|
|
427
443
|
setPreviousSelectedMentionSuggestionIndex,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../src/primitives/Composer/index.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n DetectOverflowOptions,\n UseFloatingOptions,\n} from \"@floating-ui/react-dom\";\nimport {\n autoUpdate,\n flip,\n hide,\n limitShift,\n shift,\n size,\n useFloating,\n} from \"@floating-ui/react-dom\";\nimport type { CommentAttachment, CommentBody } from \"@liveblocks/core\";\nimport { useRoom } from \"@liveblocks/react\";\nimport { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport type {\n AriaAttributes,\n ChangeEvent,\n FocusEvent,\n FormEvent,\n KeyboardEvent,\n MouseEvent,\n PointerEvent,\n SyntheticEvent,\n} from \"react\";\nimport React, {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type {\n Descendant as SlateDescendant,\n Element as SlateElement,\n} from \"slate\";\nimport {\n createEditor,\n Editor as SlateEditor,\n insertText as insertSlateText,\n Transforms as SlateTransforms,\n} from \"slate\";\nimport { withHistory } from \"slate-history\";\nimport type {\n RenderElementProps,\n RenderElementSpecificProps,\n RenderLeafProps,\n RenderPlaceholderProps,\n} from \"slate-react\";\nimport {\n Editable,\n ReactEditor,\n Slate,\n useSelected,\n useSlateStatic,\n withReact,\n} from \"slate-react\";\n\nimport { useLiveblocksUIConfig } from \"../../config\";\nimport { FLOATING_ELEMENT_COLLISION_PADDING } from \"../../constants\";\nimport { useMentionSuggestions } from \"../../shared\";\nimport { withAutoFormatting } from \"../../slate/plugins/auto-formatting\";\nimport { withAutoLinks } from \"../../slate/plugins/auto-links\";\nimport { withCustomLinks } from \"../../slate/plugins/custom-links\";\nimport { withEmptyClearFormatting } from \"../../slate/plugins/empty-clear-formatting\";\nimport type { MentionDraft } from \"../../slate/plugins/mentions\";\nimport {\n getMentionDraftAtSelection,\n insertMention,\n insertMentionCharacter,\n MENTION_CHARACTER,\n withMentions,\n} from \"../../slate/plugins/mentions\";\nimport { withPaste } from \"../../slate/plugins/paste\";\nimport { getDOMRange } from \"../../slate/utils/get-dom-range\";\nimport { isEmpty as isEditorEmpty } from \"../../slate/utils/is-empty\";\nimport { leaveMarkEdge, toggleMark } from \"../../slate/utils/marks\";\nimport type {\n ComposerBody as ComposerBodyData,\n ComposerBodyAutoLink,\n ComposerBodyCustomLink,\n ComposerBodyMention,\n} from \"../../types\";\nimport { isKey } from \"../../utils/is-key\";\nimport { Persist, useAnimationPersist, usePersist } from \"../../utils/Persist\";\nimport { Portal } from \"../../utils/Portal\";\nimport { requestSubmit } from \"../../utils/request-submit\";\nimport { useId } from \"../../utils/use-id\";\nimport { useIndex } from \"../../utils/use-index\";\nimport { useInitial } from \"../../utils/use-initial\";\nimport { useLayoutEffect } from \"../../utils/use-layout-effect\";\nimport { useRefs } from \"../../utils/use-refs\";\nimport { toAbsoluteUrl } from \"../Comment/utils\";\nimport {\n ComposerAttachmentsContext,\n ComposerContext,\n ComposerEditorContext,\n ComposerSuggestionsContext,\n useComposer,\n useComposerAttachmentsContext,\n useComposerEditorContext,\n useComposerSuggestionsContext,\n} from \"./contexts\";\nimport type {\n ComposerAttachFilesProps,\n ComposerAttachmentsDropAreaProps,\n ComposerEditorComponents,\n ComposerEditorElementProps,\n ComposerEditorLinkWrapperProps,\n ComposerEditorMentionSuggestionsWrapperProps,\n ComposerEditorMentionWrapperProps,\n ComposerEditorProps,\n ComposerFormProps,\n ComposerLinkProps,\n ComposerMentionProps,\n ComposerSubmitProps,\n ComposerSuggestionsListItemProps,\n ComposerSuggestionsListProps,\n ComposerSuggestionsProps,\n SuggestionsPosition,\n} from \"./types\";\nimport {\n commentBodyToComposerBody,\n composerBodyToCommentBody,\n getPlacementFromPosition,\n getSideAndAlignFromPlacement,\n useComposerAttachmentsDropArea,\n useComposerAttachmentsManager,\n} from \"./utils\";\n\nconst MENTION_SUGGESTIONS_POSITION: SuggestionsPosition = \"top\";\n\nconst COMPOSER_MENTION_NAME = \"ComposerMention\";\nconst COMPOSER_LINK_NAME = \"ComposerLink\";\nconst COMPOSER_SUGGESTIONS_NAME = \"ComposerSuggestions\";\nconst COMPOSER_SUGGESTIONS_LIST_NAME = \"ComposerSuggestionsList\";\nconst COMPOSER_SUGGESTIONS_LIST_ITEM_NAME = \"ComposerSuggestionsListItem\";\nconst COMPOSER_SUBMIT_NAME = \"ComposerSubmit\";\nconst COMPOSER_EDITOR_NAME = \"ComposerEditor\";\nconst COMPOSER_ATTACH_FILES_NAME = \"ComposerAttachFiles\";\nconst COMPOSER_ATTACHMENTS_DROP_AREA_NAME = \"ComposerAttachmentsDropArea\";\nconst COMPOSER_FORM_NAME = \"ComposerForm\";\n\nconst emptyCommentBody: CommentBody = {\n version: 1,\n content: [{ type: \"paragraph\", children: [{ text: \"\" }] }],\n};\n\nfunction createComposerEditor({\n createAttachments,\n pasteFilesAsAttachments,\n}: {\n createAttachments: (files: File[]) => void;\n pasteFilesAsAttachments?: boolean;\n}) {\n return withMentions(\n withCustomLinks(\n withAutoLinks(\n withAutoFormatting(\n withEmptyClearFormatting(\n withPaste(withHistory(withReact(createEditor())), {\n createAttachments,\n pasteFilesAsAttachments,\n })\n )\n )\n )\n )\n );\n}\n\nfunction ComposerEditorMentionWrapper({\n Mention,\n attributes,\n children,\n element,\n}: ComposerEditorMentionWrapperProps) {\n const isSelected = useSelected();\n\n return (\n <span {...attributes}>\n {element.id ? (\n <Mention userId={element.id} isSelected={isSelected} />\n ) : null}\n {children}\n </span>\n );\n}\n\nfunction ComposerEditorLinkWrapper({\n Link,\n attributes,\n element,\n children,\n}: ComposerEditorLinkWrapperProps) {\n const href = useMemo(\n () => toAbsoluteUrl(element.url) ?? element.url,\n [element.url]\n );\n\n return (\n <span {...attributes}>\n <Link href={href}>{children}</Link>\n </span>\n );\n}\n\nfunction ComposerEditorMentionSuggestionsWrapper({\n id,\n itemId,\n userIds,\n selectedUserId,\n setSelectedUserId,\n mentionDraft,\n onItemSelect,\n position = MENTION_SUGGESTIONS_POSITION,\n dir,\n MentionSuggestions,\n}: ComposerEditorMentionSuggestionsWrapperProps) {\n const editor = useSlateStatic();\n const { isFocused } = useComposer();\n const [content, setContent] = useState<HTMLDivElement | null>(null);\n const [contentZIndex, setContentZIndex] = useState<string>();\n const contentRef = useCallback(setContent, [setContent]);\n const { portalContainer } = useLiveblocksUIConfig();\n const floatingOptions: UseFloatingOptions = useMemo(() => {\n const detectOverflowOptions: DetectOverflowOptions = {\n padding: FLOATING_ELEMENT_COLLISION_PADDING,\n };\n\n return {\n strategy: \"fixed\",\n placement: getPlacementFromPosition(position, dir),\n middleware: [\n flip({ ...detectOverflowOptions, crossAxis: false }),\n hide(detectOverflowOptions),\n shift({\n ...detectOverflowOptions,\n limiter: limitShift(),\n }),\n size({\n ...detectOverflowOptions,\n apply({ availableWidth, availableHeight, elements }) {\n elements.floating.style.setProperty(\n \"--lb-composer-suggestions-available-width\",\n `${availableWidth}px`\n );\n elements.floating.style.setProperty(\n \"--lb-composer-suggestions-available-height\",\n `${availableHeight}px`\n );\n },\n }),\n ],\n whileElementsMounted: (...args) => {\n return autoUpdate(...args, {\n animationFrame: true,\n });\n },\n };\n }, [position, dir]);\n const {\n refs: { setReference, setFloating },\n strategy,\n isPositioned,\n placement,\n x,\n y,\n } = useFloating(floatingOptions);\n\n // Copy `z-index` from content to wrapper.\n // Inspired by https://github.com/radix-ui/primitives/blob/main/packages/react/popper/src/Popper.tsx\n useLayoutEffect(() => {\n if (content) {\n setContentZIndex(window.getComputedStyle(content).zIndex);\n }\n }, [content]);\n\n useLayoutEffect(() => {\n if (!mentionDraft) {\n return;\n }\n\n const domRange = getDOMRange(editor, mentionDraft.range);\n\n if (domRange) {\n setReference({\n getBoundingClientRect: () => domRange.getBoundingClientRect(),\n getClientRects: () => domRange.getClientRects(),\n });\n }\n }, [setReference, editor, mentionDraft]);\n\n return (\n <Persist>\n {mentionDraft?.range && isFocused && userIds ? (\n <ComposerSuggestionsContext.Provider\n value={{\n id,\n itemId,\n selectedValue: selectedUserId,\n setSelectedValue: setSelectedUserId,\n onItemSelect,\n placement,\n dir,\n ref: contentRef,\n }}\n >\n <Portal\n ref={setFloating}\n container={portalContainer}\n style={{\n position: strategy,\n top: 0,\n left: 0,\n transform: isPositioned\n ? `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`\n : \"translate3d(0, -200%, 0)\",\n minWidth: \"max-content\",\n zIndex: contentZIndex,\n }}\n >\n <MentionSuggestions\n userIds={userIds}\n selectedUserId={selectedUserId}\n />\n </Portal>\n </ComposerSuggestionsContext.Provider>\n ) : null}\n </Persist>\n );\n}\n\nfunction ComposerEditorElement({\n Mention,\n Link,\n ...props\n}: ComposerEditorElementProps) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const { attributes, children, element } = props;\n\n switch (element.type) {\n case \"mention\":\n return (\n <ComposerEditorMentionWrapper\n Mention={Mention}\n {...(props as RenderElementSpecificProps<ComposerBodyMention>)}\n />\n );\n case \"auto-link\":\n case \"custom-link\":\n return (\n <ComposerEditorLinkWrapper\n Link={Link}\n {...(props as RenderElementSpecificProps<\n ComposerBodyAutoLink | ComposerBodyCustomLink\n >)}\n />\n );\n case \"paragraph\":\n return (\n <p {...attributes} style={{ position: \"relative\" }}>\n {children}\n </p>\n );\n default:\n return null;\n }\n}\n\n// <code><s><em><strong>text</strong></s></em></code>\nfunction ComposerEditorLeaf({ attributes, children, leaf }: RenderLeafProps) {\n if (leaf.bold) {\n children = <strong>{children}</strong>;\n }\n\n if (leaf.italic) {\n children = <em>{children}</em>;\n }\n\n if (leaf.strikethrough) {\n children = <s>{children}</s>;\n }\n\n if (leaf.code) {\n children = <code>{children}</code>;\n }\n\n return <span {...attributes}>{children}</span>;\n}\n\nfunction ComposerEditorPlaceholder({\n attributes,\n children,\n}: RenderPlaceholderProps) {\n const { opacity: _opacity, ...style } = attributes.style;\n\n return (\n <span {...attributes} style={style} data-placeholder=\"\">\n {children}\n </span>\n );\n}\n\n/**\n * Displays mentions within `Composer.Editor`.\n *\n * @example\n * <Composer.Mention>@{userId}</Composer.Mention>\n */\nconst ComposerMention = forwardRef<HTMLSpanElement, ComposerMentionProps>(\n ({ children, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"span\";\n const isSelected = useSelected();\n\n return (\n <Component\n data-selected={isSelected || undefined}\n {...props}\n ref={forwardedRef}\n >\n {children}\n </Component>\n );\n }\n);\n\n/**\n * Displays links within `Composer.Editor`.\n *\n * @example\n * <Composer.Link href={href}>{children}</Composer.Link>\n */\nconst ComposerLink = forwardRef<HTMLAnchorElement, ComposerLinkProps>(\n ({ children, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"a\";\n\n return (\n <Component\n target=\"_blank\"\n rel=\"noopener noreferrer nofollow\"\n {...props}\n ref={forwardedRef}\n >\n {children}\n </Component>\n );\n }\n);\n\n/**\n * Contains suggestions within `Composer.Editor`.\n */\nconst ComposerSuggestions = forwardRef<\n HTMLDivElement,\n ComposerSuggestionsProps\n>(({ children, style, asChild, ...props }, forwardedRef) => {\n const [isPresent] = usePersist();\n const ref = useRef<HTMLDivElement>(null);\n const {\n ref: contentRef,\n placement,\n dir,\n } = useComposerSuggestionsContext(COMPOSER_SUGGESTIONS_NAME);\n const mergedRefs = useRefs(forwardedRef, contentRef, ref);\n const [side, align] = useMemo(\n () => getSideAndAlignFromPlacement(placement),\n [placement]\n );\n const Component = asChild ? Slot : \"div\";\n useAnimationPersist(ref);\n\n return (\n <Component\n dir={dir}\n {...props}\n data-state={isPresent ? \"open\" : \"closed\"}\n data-side={side}\n data-align={align}\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n maxHeight: \"var(--lb-composer-suggestions-available-height)\",\n overflowY: \"auto\",\n ...style,\n }}\n ref={mergedRefs}\n >\n {children}\n </Component>\n );\n});\n\n/**\n * Displays a list of suggestions within `Composer.Editor`.\n *\n * @example\n * <Composer.SuggestionsList>\n * {userIds.map((userId) => (\n * <Composer.SuggestionsListItem key={userId} value={userId}>\n * @{userId}\n * </Composer.SuggestionsListItem>\n * ))}\n * </Composer.SuggestionsList>\n */\nconst ComposerSuggestionsList = forwardRef<\n HTMLUListElement,\n ComposerSuggestionsListProps\n>(({ children, asChild, ...props }, forwardedRef) => {\n const { id } = useComposerSuggestionsContext(COMPOSER_SUGGESTIONS_LIST_NAME);\n const Component = asChild ? Slot : \"ul\";\n\n return (\n <Component\n role=\"listbox\"\n id={id}\n aria-label=\"Suggestions list\"\n {...props}\n ref={forwardedRef}\n >\n {children}\n </Component>\n );\n});\n\n/**\n * Displays a suggestion within `Composer.SuggestionsList`.\n *\n * @example\n * <Composer.SuggestionsListItem key={userId} value={userId}>\n * @{userId}\n * </Composer.SuggestionsListItem>\n */\nconst ComposerSuggestionsListItem = forwardRef<\n HTMLLIElement,\n ComposerSuggestionsListItemProps\n>(\n (\n { value, children, onPointerMove, onPointerDown, asChild, ...props },\n forwardedRef\n ) => {\n const ref = useRef<HTMLLIElement>(null);\n const mergedRefs = useRefs(forwardedRef, ref);\n const { selectedValue, setSelectedValue, itemId, onItemSelect } =\n useComposerSuggestionsContext(COMPOSER_SUGGESTIONS_LIST_ITEM_NAME);\n const Component = asChild ? Slot : \"li\";\n const isSelected = useMemo(\n () => selectedValue === value,\n [selectedValue, value]\n );\n // TODO: Support props.id if provided, it will need to be sent up to Composer.Editor to use it in aria-activedescendant\n const id = useMemo(() => itemId(value), [itemId, value]);\n\n useEffect(() => {\n if (ref?.current && isSelected) {\n ref.current.scrollIntoView({ block: \"nearest\" });\n }\n }, [isSelected]);\n\n const handlePointerMove = useCallback(\n (event: PointerEvent<HTMLLIElement>) => {\n onPointerMove?.(event);\n\n if (!event.isDefaultPrevented()) {\n setSelectedValue(value);\n }\n },\n [onPointerMove, setSelectedValue, value]\n );\n\n const handlePointerDown = useCallback(\n (event: PointerEvent<HTMLLIElement>) => {\n onPointerDown?.(event);\n\n if (!event.isDefaultPrevented()) {\n const target = event.target as HTMLElement;\n\n if (target.hasPointerCapture(event.pointerId)) {\n target.releasePointerCapture(event.pointerId);\n }\n\n if (event.button === 0 && event.ctrlKey === false) {\n onItemSelect(value);\n\n event.preventDefault();\n }\n }\n },\n [onItemSelect, onPointerDown, value]\n );\n\n return (\n <Component\n role=\"option\"\n id={id}\n data-selected={isSelected || undefined}\n aria-selected={isSelected || undefined}\n onPointerMove={handlePointerMove}\n onPointerDown={handlePointerDown}\n {...props}\n ref={mergedRefs}\n >\n {children}\n </Component>\n );\n }\n);\n\nconst defaultEditorComponents: ComposerEditorComponents = {\n Link: ({ href, children }) => {\n return <ComposerLink href={href}>{children}</ComposerLink>;\n },\n Mention: ({ userId }) => {\n return (\n <ComposerMention>\n {MENTION_CHARACTER}\n {userId}\n </ComposerMention>\n );\n },\n MentionSuggestions: ({ userIds }) => {\n return userIds.length > 0 ? (\n <ComposerSuggestions>\n <ComposerSuggestionsList>\n {userIds.map((userId) => (\n <ComposerSuggestionsListItem key={userId} value={userId}>\n {userId}\n </ComposerSuggestionsListItem>\n ))}\n </ComposerSuggestionsList>\n </ComposerSuggestions>\n ) : null;\n },\n};\n\n/**\n * Displays the composer's editor.\n *\n * @example\n * <Composer.Editor placeholder=\"Write a comment…\" />\n */\nconst ComposerEditor = forwardRef<HTMLDivElement, ComposerEditorProps>(\n (\n {\n defaultValue,\n onKeyDown,\n onFocus,\n onBlur,\n disabled,\n autoFocus,\n components,\n dir,\n ...props\n },\n forwardedRef\n ) => {\n const { editor, validate, setFocused } = useComposerEditorContext();\n const {\n submit,\n focus,\n select,\n canSubmit,\n isDisabled: isComposerDisabled,\n isFocused,\n } = useComposer();\n const isDisabled = isComposerDisabled || disabled;\n const initialBody = useInitial(defaultValue ?? emptyCommentBody);\n const initialEditorValue = useMemo(() => {\n return commentBodyToComposerBody(initialBody);\n }, [initialBody]);\n const { Link, Mention, MentionSuggestions } = useMemo(\n () => ({ ...defaultEditorComponents, ...components }),\n [components]\n );\n\n const [mentionDraft, setMentionDraft] = useState<MentionDraft>();\n const mentionSuggestions = useMentionSuggestions(mentionDraft?.text);\n const [\n selectedMentionSuggestionIndex,\n setPreviousSelectedMentionSuggestionIndex,\n setNextSelectedMentionSuggestionIndex,\n setSelectedMentionSuggestionIndex,\n ] = useIndex(0, mentionSuggestions?.length ?? 0);\n const id = useId();\n const suggestionsListId = useMemo(\n () => `liveblocks-suggestions-list-${id}`,\n [id]\n );\n const suggestionsListItemId = useCallback(\n (userId?: string) =>\n userId ? `liveblocks-suggestions-list-item-${id}-${userId}` : undefined,\n [id]\n );\n const renderElement = useCallback(\n (props: RenderElementProps) => {\n return (\n <ComposerEditorElement Mention={Mention} Link={Link} {...props} />\n );\n },\n [Link, Mention]\n );\n\n const handleChange = useCallback(\n (value: SlateDescendant[]) => {\n validate(value as SlateElement[]);\n\n setMentionDraft(getMentionDraftAtSelection(editor));\n },\n [editor, validate]\n );\n\n const createMention = useCallback(\n (userId?: string) => {\n if (!mentionDraft || !userId) {\n return;\n }\n\n SlateTransforms.select(editor, mentionDraft.range);\n insertMention(editor, userId);\n setMentionDraft(undefined);\n setSelectedMentionSuggestionIndex(0);\n },\n [editor, mentionDraft, setSelectedMentionSuggestionIndex]\n );\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n // Allow leaving marks with ArrowLeft\n if (isKey(event, \"ArrowLeft\")) {\n leaveMarkEdge(editor, \"start\");\n }\n\n // Allow leaving marks with ArrowRight\n if (isKey(event, \"ArrowRight\")) {\n leaveMarkEdge(editor, \"end\");\n }\n\n if (mentionDraft && mentionSuggestions?.length) {\n // Select the next mention suggestion on ArrowDown\n if (isKey(event, \"ArrowDown\")) {\n event.preventDefault();\n setNextSelectedMentionSuggestionIndex();\n }\n\n // Select the previous mention suggestion on ArrowUp\n if (isKey(event, \"ArrowUp\")) {\n event.preventDefault();\n setPreviousSelectedMentionSuggestionIndex();\n }\n\n // Create a mention on Enter/Tab\n if (isKey(event, \"Enter\") || isKey(event, \"Tab\")) {\n event.preventDefault();\n\n const userId = mentionSuggestions?.[selectedMentionSuggestionIndex];\n createMention(userId);\n }\n\n // Close the suggestions on Escape\n if (isKey(event, \"Escape\")) {\n event.preventDefault();\n setMentionDraft(undefined);\n setSelectedMentionSuggestionIndex(0);\n }\n } else {\n // Blur the editor on Escape\n if (isKey(event, \"Escape\")) {\n event.preventDefault();\n ReactEditor.blur(editor);\n }\n\n // Submit the editor on Enter\n if (isKey(event, \"Enter\", { shift: false })) {\n // Even if submitting is not possible, don't do anything else on Enter. (e.g. creating a new line)\n event.preventDefault();\n\n if (canSubmit) {\n submit();\n }\n }\n\n // Create a new line on Shift + Enter\n if (isKey(event, \"Enter\", { shift: true })) {\n event.preventDefault();\n editor.insertBreak();\n }\n\n // Toggle bold on Command/Control + B\n if (isKey(event, \"b\", { mod: true })) {\n event.preventDefault();\n toggleMark(editor, \"bold\");\n }\n\n // Toggle italic on Command/Control + I\n if (isKey(event, \"i\", { mod: true })) {\n event.preventDefault();\n toggleMark(editor, \"italic\");\n }\n\n // Toggle strikethrough on Command/Control + Shift + S\n if (isKey(event, \"s\", { mod: true, shift: true })) {\n event.preventDefault();\n toggleMark(editor, \"strikethrough\");\n }\n\n // Toggle code on Command/Control + E\n if (isKey(event, \"e\", { mod: true })) {\n event.preventDefault();\n toggleMark(editor, \"code\");\n }\n }\n },\n [\n createMention,\n editor,\n canSubmit,\n mentionDraft,\n mentionSuggestions,\n selectedMentionSuggestionIndex,\n onKeyDown,\n setNextSelectedMentionSuggestionIndex,\n setPreviousSelectedMentionSuggestionIndex,\n setSelectedMentionSuggestionIndex,\n submit,\n ]\n );\n\n const handleFocus = useCallback(\n (event: FocusEvent<HTMLDivElement>) => {\n onFocus?.(event);\n\n if (!event.isDefaultPrevented()) {\n setFocused(true);\n }\n },\n [onFocus, setFocused]\n );\n\n const handleBlur = useCallback(\n (event: FocusEvent<HTMLDivElement>) => {\n onBlur?.(event);\n\n if (!event.isDefaultPrevented()) {\n setFocused(false);\n }\n },\n [onBlur, setFocused]\n );\n\n const selectedMentionSuggestionUserId = useMemo(\n () => mentionSuggestions?.[selectedMentionSuggestionIndex],\n [selectedMentionSuggestionIndex, mentionSuggestions]\n );\n const setSelectedMentionSuggestionUserId = useCallback(\n (userId: string) => {\n const index = mentionSuggestions?.indexOf(userId);\n\n if (index !== undefined && index >= 0) {\n setSelectedMentionSuggestionIndex(index);\n }\n },\n [setSelectedMentionSuggestionIndex, mentionSuggestions]\n );\n\n const propsWhileSuggesting: AriaAttributes = useMemo(\n () =>\n mentionDraft\n ? {\n role: \"combobox\",\n \"aria-autocomplete\": \"list\",\n \"aria-expanded\": true,\n \"aria-controls\": suggestionsListId,\n \"aria-activedescendant\": suggestionsListItemId(\n selectedMentionSuggestionUserId\n ),\n }\n : {},\n [\n mentionDraft,\n suggestionsListId,\n suggestionsListItemId,\n selectedMentionSuggestionUserId,\n ]\n );\n\n useImperativeHandle(forwardedRef, () => {\n return ReactEditor.toDOMNode(editor, editor) as HTMLDivElement;\n }, [editor]);\n\n // Manually focus the editor when `autoFocus` is true\n useEffect(() => {\n if (autoFocus) {\n focus();\n }\n }, [autoFocus, editor, focus]);\n\n // Manually add a selection in the editor if the selection\n // is still empty after being focused\n useEffect(() => {\n if (isFocused && editor.selection === null) {\n select();\n }\n }, [editor, select, isFocused]);\n\n return (\n <Slate\n editor={editor}\n initialValue={initialEditorValue}\n onChange={handleChange}\n >\n <Editable\n dir={dir}\n enterKeyHint={mentionDraft ? \"enter\" : \"send\"}\n autoCapitalize=\"sentences\"\n aria-label=\"Composer editor\"\n data-focused={isFocused || undefined}\n data-disabled={isDisabled || undefined}\n {...propsWhileSuggesting}\n {...props}\n readOnly={isDisabled}\n disabled={isDisabled}\n onKeyDown={handleKeyDown}\n onFocus={handleFocus}\n onBlur={handleBlur}\n renderElement={renderElement}\n renderLeaf={ComposerEditorLeaf}\n renderPlaceholder={ComposerEditorPlaceholder}\n />\n <ComposerEditorMentionSuggestionsWrapper\n dir={dir}\n mentionDraft={mentionDraft}\n selectedUserId={selectedMentionSuggestionUserId}\n setSelectedUserId={setSelectedMentionSuggestionUserId}\n userIds={mentionSuggestions}\n id={suggestionsListId}\n itemId={suggestionsListItemId}\n onItemSelect={createMention}\n MentionSuggestions={MentionSuggestions}\n />\n </Slate>\n );\n }\n);\n\nconst MAX_ATTACHMENTS = 10;\nconst MAX_ATTACHMENT_SIZE = 1024 * 1024 * 1024; // 1 GB\n\n/**\n * Surrounds the composer's content and handles submissions.\n *\n * @example\n * <Composer.Form onComposerSubmit={({ body }) => {}}>\n *\t <Composer.Editor />\n * <Composer.Submit />\n * </Composer.Form>\n */\nconst ComposerForm = forwardRef<HTMLFormElement, ComposerFormProps>(\n (\n {\n children,\n onSubmit,\n onComposerSubmit,\n defaultAttachments = [],\n pasteFilesAsAttachments,\n disabled,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"form\";\n const room = useRoom();\n const [isEmpty, setEmpty] = useState(true);\n const [isSubmitting, setSubmitting] = useState(false);\n const [isFocused, setFocused] = useState(false);\n // Later: Offer as Composer.Form props: { maxAttachments: number; maxAttachmentSize: number; supportedAttachmentMimeTypes: string[]; }\n const maxAttachments = MAX_ATTACHMENTS;\n const maxAttachmentSize = MAX_ATTACHMENT_SIZE;\n const {\n attachments,\n isUploadingAttachments,\n addAttachments,\n removeAttachment,\n clearAttachments,\n } = useComposerAttachmentsManager(defaultAttachments, {\n maxFileSize: maxAttachmentSize,\n });\n const numberOfAttachments = attachments.length;\n const hasMaxAttachments = numberOfAttachments >= maxAttachments;\n const isDisabled = useMemo(() => {\n const self = room.getSelf();\n const canComment = self?.canComment ?? true;\n\n return isSubmitting || disabled || !canComment;\n }, [isSubmitting, disabled, room]);\n const canSubmit = useMemo(() => {\n return !isEmpty && !isUploadingAttachments;\n }, [isEmpty, isUploadingAttachments]);\n const ref = useRef<HTMLFormElement>(null);\n const mergedRefs = useRefs(forwardedRef, ref);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const createAttachments = useCallback(\n (files: File[]) => {\n if (!files.length) {\n return;\n }\n\n const numberOfAcceptedFiles = Math.max(\n 0,\n maxAttachments - numberOfAttachments\n );\n\n files.splice(numberOfAcceptedFiles);\n\n const attachments = files.map((file) => room.prepareAttachment(file));\n\n addAttachments(attachments);\n },\n [addAttachments, maxAttachments, numberOfAttachments, room]\n );\n\n const createAttachmentsRef = useRef(createAttachments);\n\n useEffect(() => {\n createAttachmentsRef.current = createAttachments;\n }, [createAttachments]);\n\n const stableCreateAttachments = useCallback((files: File[]) => {\n createAttachmentsRef.current(files);\n }, []);\n\n const editor = useInitial(() =>\n createComposerEditor({\n createAttachments: stableCreateAttachments,\n pasteFilesAsAttachments,\n })\n );\n\n const validate = useCallback(\n (value: SlateElement[]) => {\n setEmpty(isEditorEmpty(editor, value));\n },\n [editor]\n );\n\n const submit = useCallback(() => {\n if (!canSubmit) {\n return;\n }\n\n // We need to wait for the next frame in some cases like when composing diacritics,\n // we want any native handling to be done first while still being handled on `keydown`.\n requestAnimationFrame(() => {\n if (ref.current) {\n requestSubmit(ref.current);\n }\n });\n }, [canSubmit]);\n\n const clear = useCallback(() => {\n SlateTransforms.delete(editor, {\n at: {\n anchor: SlateEditor.start(editor, []),\n focus: SlateEditor.end(editor, []),\n },\n });\n }, [editor]);\n\n const select = useCallback(() => {\n SlateTransforms.select(editor, {\n anchor: SlateEditor.end(editor, []),\n focus: SlateEditor.end(editor, []),\n });\n }, [editor]);\n\n const focus = useCallback(\n (resetSelection = true) => {\n if (!ReactEditor.isFocused(editor)) {\n SlateTransforms.select(\n editor,\n resetSelection || !editor.selection\n ? SlateEditor.end(editor, [])\n : editor.selection\n );\n ReactEditor.focus(editor);\n }\n },\n [editor]\n );\n\n const blur = useCallback(() => {\n ReactEditor.blur(editor);\n }, [editor]);\n\n const createMention = useCallback(() => {\n if (disabled) {\n return;\n }\n\n focus();\n insertMentionCharacter(editor);\n }, [disabled, editor, focus]);\n\n const insertText = useCallback(\n (text: string) => {\n if (disabled) {\n return;\n }\n\n focus(false);\n insertSlateText(editor, text);\n },\n [disabled, editor, focus]\n );\n\n const attachFiles = useCallback(() => {\n if (disabled) {\n return;\n }\n\n if (fileInputRef.current) {\n fileInputRef.current.click();\n }\n }, [disabled]);\n\n const handleAttachmentsInputChange = useCallback(\n (event: ChangeEvent<HTMLInputElement>) => {\n if (disabled) {\n return;\n }\n\n if (event.target.files) {\n createAttachments(Array.from(event.target.files));\n\n // Reset the input value to allow selecting the same file(s) again\n event.target.value = \"\";\n }\n },\n [createAttachments, disabled]\n );\n\n const onSubmitEnd = useCallback(() => {\n clear();\n blur();\n clearAttachments();\n setSubmitting(false);\n }, [blur, clear, clearAttachments]);\n\n const handleSubmit = useCallback(\n (event: FormEvent<HTMLFormElement>) => {\n if (disabled) {\n return;\n }\n\n // In some situations (e.g. pressing Enter while composing diacritics), it's possible\n // for the form to be submitted as empty even though we already checked whether the\n // editor was empty when handling the key press.\n const isEmpty = isEditorEmpty(editor, editor.children);\n\n // We even prevent the user's `onSubmit` handler from being called if the editor is empty.\n if (isEmpty) {\n event.preventDefault();\n\n return;\n }\n\n onSubmit?.(event);\n\n if (!onComposerSubmit || event.isDefaultPrevented()) {\n event.preventDefault();\n\n return;\n }\n\n const body = composerBodyToCommentBody(\n editor.children as ComposerBodyData\n );\n // Only non-local attachments are included to be submitted.\n const commentAttachments: CommentAttachment[] = attachments\n .filter(\n (attachment) =>\n attachment.type === \"attachment\" ||\n (attachment.type === \"localAttachment\" &&\n attachment.status === \"uploaded\")\n )\n .map((attachment) => {\n return {\n id: attachment.id,\n type: \"attachment\",\n mimeType: attachment.mimeType,\n size: attachment.size,\n name: attachment.name,\n };\n });\n\n const promise = onComposerSubmit(\n { body, attachments: commentAttachments },\n event\n );\n\n event.preventDefault();\n\n if (promise) {\n setSubmitting(true);\n promise.then(onSubmitEnd);\n } else {\n onSubmitEnd();\n }\n },\n [disabled, editor, attachments, onComposerSubmit, onSubmit, onSubmitEnd]\n );\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <ComposerEditorContext.Provider\n value={{\n editor,\n validate,\n setFocused,\n }}\n >\n <ComposerAttachmentsContext.Provider\n value={{\n createAttachments,\n isUploadingAttachments,\n hasMaxAttachments,\n maxAttachments,\n maxAttachmentSize,\n }}\n >\n <ComposerContext.Provider\n value={{\n isDisabled,\n isFocused,\n isEmpty,\n canSubmit,\n submit,\n clear,\n select,\n focus,\n blur,\n createMention,\n insertText,\n attachments,\n attachFiles,\n removeAttachment,\n }}\n >\n <Component {...props} onSubmit={handleSubmit} ref={mergedRefs}>\n <input\n type=\"file\"\n multiple\n ref={fileInputRef}\n onChange={handleAttachmentsInputChange}\n onClick={stopPropagation}\n tabIndex={-1}\n style={{ display: \"none\" }}\n />\n <Slottable>{children}</Slottable>\n </Component>\n </ComposerContext.Provider>\n </ComposerAttachmentsContext.Provider>\n </ComposerEditorContext.Provider>\n );\n }\n);\n\n/**\n * A button to submit the composer.\n *\n * @example\n * <Composer.Submit>Send</Composer.Submit>\n */\nconst ComposerSubmit = forwardRef<HTMLButtonElement, ComposerSubmitProps>(\n ({ children, disabled, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"button\";\n const { canSubmit, isDisabled: isComposerDisabled } = useComposer();\n const isDisabled = isComposerDisabled || disabled || !canSubmit;\n\n return (\n <Component\n type=\"submit\"\n {...props}\n ref={forwardedRef}\n disabled={isDisabled}\n >\n {children}\n </Component>\n );\n }\n);\n\n/**\n * A button which opens a file picker to create attachments.\n *\n * @example\n * <Composer.AttachFiles>Attach files</Composer.AttachFiles>\n */\nconst ComposerAttachFiles = forwardRef<\n HTMLButtonElement,\n ComposerAttachFilesProps\n>(({ children, onClick, disabled, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"button\";\n const { hasMaxAttachments } = useComposerAttachmentsContext();\n const { isDisabled: isComposerDisabled, attachFiles } = useComposer();\n const isDisabled = isComposerDisabled || hasMaxAttachments || disabled;\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n attachFiles();\n }\n },\n [attachFiles, onClick]\n );\n\n return (\n <Component\n type=\"button\"\n {...props}\n onClick={handleClick}\n ref={forwardedRef}\n disabled={isDisabled}\n >\n {children}\n </Component>\n );\n});\n\n/**\n * A drop area which accepts files to create attachments.\n *\n * @example\n * <Composer.AttachmentsDropArea>\n * Drop files here\n * </Composer.AttachmentsDropArea>\n */\nconst ComposerAttachmentsDropArea = forwardRef<\n HTMLDivElement,\n ComposerAttachmentsDropAreaProps\n>(\n (\n {\n onDragEnter,\n onDragLeave,\n onDragOver,\n onDrop,\n disabled,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"div\";\n const { isDisabled: isComposerDisabled } = useComposer();\n const isDisabled = isComposerDisabled || disabled;\n const [, dropAreaProps] = useComposerAttachmentsDropArea({\n onDragEnter,\n onDragLeave,\n onDragOver,\n onDrop,\n disabled: isDisabled,\n });\n\n return (\n <Component\n {...dropAreaProps}\n data-disabled={isDisabled ? \"\" : undefined}\n {...props}\n ref={forwardedRef}\n />\n );\n }\n);\n\nif (process.env.NODE_ENV !== \"production\") {\n ComposerAttachFiles.displayName = COMPOSER_ATTACH_FILES_NAME;\n ComposerAttachmentsDropArea.displayName = COMPOSER_ATTACHMENTS_DROP_AREA_NAME;\n ComposerEditor.displayName = COMPOSER_EDITOR_NAME;\n ComposerForm.displayName = COMPOSER_FORM_NAME;\n ComposerMention.displayName = COMPOSER_MENTION_NAME;\n ComposerLink.displayName = COMPOSER_LINK_NAME;\n ComposerSubmit.displayName = COMPOSER_SUBMIT_NAME;\n ComposerSuggestions.displayName = COMPOSER_SUGGESTIONS_NAME;\n ComposerSuggestionsList.displayName = COMPOSER_SUGGESTIONS_LIST_NAME;\n ComposerSuggestionsListItem.displayName = COMPOSER_SUGGESTIONS_LIST_ITEM_NAME;\n}\n\n// NOTE: Every export from this file will be available publicly as Composer.*\nexport {\n ComposerAttachFiles as AttachFiles,\n ComposerAttachmentsDropArea as AttachmentsDropArea,\n ComposerEditor as Editor,\n ComposerForm as Form,\n ComposerLink as Link,\n ComposerMention as Mention,\n ComposerSubmit as Submit,\n ComposerSuggestions as Suggestions,\n ComposerSuggestionsList as SuggestionsList,\n ComposerSuggestionsListItem as SuggestionsListItem,\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuIA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAsC;AAC3B;AAEX;AAEA;AAA8B;AAC5B;AAEF;AAIE;AAAO;AACL;AACE;AACE;AACE;AACoD;AAChD;AACA;AACD;AACH;AACF;AACF;AACF;AAEJ;AAEA;AAAsC;AACpC;AACA;AACA;AAEF;AACE;AAEA;AACG;AAAS;AAEL;AAAwB;AAAI;AAKrC;AAEA;AAAmC;AACjC;AACA;AACA;AAEF;AACE;AAAa;AACiC;AAChC;AAGd;AACG;AAAS;AACP;AAAK;AAGZ;AAEA;AAAiD;AAC/C;AACA;AACA;AACA;AACA;AACA;AACA;AACW;AACX;AAEF;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACE;AAAqD;AAC1C;AAGX;AAAO;AACK;AACuC;AACrC;AACyC;AACzB;AACpB;AACD;AACiB;AACrB;AACI;AACA;AAED;AAAwB;AACtB;AACG;AAEL;AAAwB;AACtB;AACG;AACL;AACF;AACD;AACH;AAEE;AAA2B;AACT;AACjB;AACH;AACF;AAEF;AAAM;AAC8B;AAClC;AACA;AACA;AACA;AACA;AAKF;AACE;AACE;AAAwD;AAC1D;AAGF;AACE;AACE;AAAA;AAGF;AAEA;AACE;AAAa;AACiD;AACd;AAC/C;AACH;AAGF;AAGO;AACQ;AACL;AACA;AACe;AACG;AAClB;AACA;AACA;AACK;AACP;AAEC;AACM;AACM;AACJ;AACK;AACL;AACC;AAGF;AACM;AACF;AACV;AAEC;AACC;AACA;AAOd;AAEA;AAA+B;AAC7B;AACA;AAEF;AAEE;AAEA;AAAsB;AAElB;AACG;AACC;AACK;AACP;AAEC;AAEH;AACG;AACC;AACK;AAGP;AAGF;AACG;AAAM;AAA0C;AAEjD;AAGF;AAAO;AAEb;AAGA;AACE;AACE;AAA6B;AAG/B;AACE;AAAyB;AAG3B;AACE;AAAwB;AAG1B;AACE;AAA2B;AAG7B;AAAQ;AAAS;AACnB;AAEA;AAAmC;AACjC;AAEF;AACE;AAEA;AACG;AAAS;AAAY;AAA+B;AAIzD;AAQA;AAAwB;AAEpB;AACA;AAEA;AACG;AAC8B;AACzB;AACC;AAGP;AAGN;AAQA;AAAqB;AAEjB;AAEA;AACG;AACQ;AACH;AACA;AACC;AAGP;AAGN;AAKM;AAIJ;AACA;AACA;AAAM;AACC;AACL;AACA;AAEF;AACA;AAAsB;AACwB;AAClC;AAEZ;AACA;AAEA;AACG;AACC;AACI;AAC6B;AACtB;AACC;AACL;AACI;AACM;AACJ;AACA;AACR;AACL;AACK;AAKX;AAcM;AAIJ;AACA;AAEA;AACG;AACM;AACL;AACW;AACP;AACC;AAKX;AAUA;AAAoC;AAQhC;AACA;AACA;AAEA;AACA;AAAmB;AACO;AACH;AAGvB;AAEA;AACE;AACE;AAA+C;AACjD;AAGF;AAA0B;AAEtB;AAEA;AACE;AAAsB;AACxB;AACF;AACuC;AAGzC;AAA0B;AAEtB;AAEA;AACE;AAEA;AACE;AAA4C;AAG9C;AACE;AAEA;AAAqB;AACvB;AACF;AACF;AACmC;AAGrC;AACG;AACM;AACL;AAC6B;AACA;AACd;AACA;AACX;AACC;AAGP;AAGN;AAEA;AAA0D;AAEtD;AAAQ;AAAa;AAAsB;AAC7C;AAEE;AAIE;AAEJ;AAEE;AAIS;AAAiC;AAAe;AAMrD;AAER;AAQA;AAAuB;AAEnB;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AAAM;AACJ;AACA;AACA;AACA;AACY;AACZ;AAEF;AACA;AACA;AACE;AAA4C;AAE9C;AAA8C;AACO;AACxC;AAGb;AACA;AACA;AAAM;AACJ;AACA;AACA;AACA;AAEF;AACA;AAA0B;AACa;AAClC;AAEL;AAA8B;AAEoC;AAC7D;AAEL;AAAsB;AAElB;AACG;AAAsB;AAAkB;AAAgB;AAAO;AAEpE;AACc;AAGhB;AAAqB;AAEjB;AAEA;AAAkD;AACpD;AACiB;AAGnB;AAAsB;AAElB;AACE;AAAA;AAGF;AACA;AACA;AACA;AAAmC;AACrC;AACwD;AAG1D;AAAsB;AAElB;AAEA;AACE;AAAA;AAIF;AACE;AAA6B;AAI/B;AACE;AAA2B;AAG7B;AAEE;AACE;AACA;AAAsC;AAIxC;AACE;AACA;AAA0C;AAI5C;AACE;AAEA;AACA;AAAoB;AAItB;AACE;AACA;AACA;AAAmC;AACrC;AAGA;AACE;AACA;AAAuB;AAIzB;AAEE;AAEA;AACE;AAAO;AACT;AAIF;AACE;AACA;AAAmB;AAIrB;AACE;AACA;AAAyB;AAI3B;AACE;AACA;AAA2B;AAI7B;AACE;AACA;AAAkC;AAIpC;AACE;AACA;AAAyB;AAC3B;AACF;AACF;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAGF;AAAoB;AAEhB;AAEA;AACE;AAAe;AACjB;AACF;AACoB;AAGtB;AAAmB;AAEf;AAEA;AACE;AAAgB;AAClB;AACF;AACmB;AAGrB;AAAwC;AACX;AACwB;AAErD;AAA2C;AAEvC;AAEA;AACE;AAAuC;AACzC;AACF;AACsD;AAGxD;AAA6C;AAGrC;AACQ;AACe;AACJ;AACA;AACQ;AACvB;AACF;AAED;AACP;AACE;AACA;AACA;AACA;AACF;AAGF;AACE;AAA2C;AAI7C;AACE;AACE;AAAM;AACR;AAKF;AACE;AACE;AAAO;AACT;AAGF;AACG;AACC;AACc;AACJ;AAET;AACC;AACuC;AACxB;AACJ;AACgB;AACE;AACzB;AACA;AACM;AACA;AACC;AACF;AACD;AACR;AACY;AACO;AAEpB;AACC;AACA;AACgB;AACG;AACV;AACL;AACI;AACM;AACd;AAEJ;AAGN;AAEA;AACA;AAWA;AAAqB;AAEjB;AACE;AACA;AACA;AACsB;AACtB;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAM;AACJ;AACA;AACA;AACA;AACA;AACoD;AACvC;AAEf;AACA;AACA;AACE;AACA;AAEA;AAAoC;AAEtC;AACE;AAAoB;AAEtB;AACA;AACA;AAEA;AAA0B;AAEtB;AACE;AAAA;AAGF;AAAmC;AACjC;AACiB;AAGnB;AAEA;AAEA;AAA0B;AAC5B;AAC0D;AAG5D;AAEA;AACE;AAA+B;AAGjC;AACE;AAAkC;AAGpC;AAAe;AACQ;AACA;AACnB;AACD;AAGH;AAAiB;AAEb;AAAqC;AACvC;AACO;AAGT;AACE;AACE;AAAA;AAKF;AACE;AACE;AAAyB;AAC3B;AACD;AAGH;AACE;AAA+B;AACzB;AACkC;AACH;AACnC;AACD;AAGH;AACE;AAA+B;AACK;AACD;AAClC;AAGH;AAAc;AAEV;AACE;AAAgB;AACd;AAGW;AAEb;AAAwB;AAC1B;AACF;AACO;AAGT;AACE;AAAuB;AAGzB;AACE;AACE;AAAA;AAGF;AACA;AAA6B;AAG/B;AAAmB;AAEf;AACE;AAAA;AAGF;AACA;AAA4B;AAC9B;AACwB;AAG1B;AACE;AACE;AAAA;AAGF;AACE;AAA2B;AAC7B;AAGF;AAAqC;AAEjC;AACE;AAAA;AAGF;AACE;AAGA;AAAqB;AACvB;AACF;AAC4B;AAG9B;AACE;AACA;AACA;AACA;AAAmB;AAGrB;AAAqB;AAEjB;AACE;AAAA;AAMF;AAGA;AACE;AAEA;AAAA;AAGF;AAEA;AACE;AAEA;AAAA;AAGF;AAAa;AACJ;AAGT;AACG;AAI2B;AAG1B;AAAO;AACU;AACT;AACe;AACJ;AACA;AACnB;AAGJ;AAAgB;AAC0B;AACxC;AAGF;AAEA;AACE;AACA;AAAwB;AAExB;AAAY;AACd;AACF;AACuE;AAGzE;AACE;AAAsB;AAGxB;AACG;AACQ;AACL;AACA;AACA;AACF;AAEC;AACQ;AACL;AACA;AACA;AACA;AACA;AACF;AAEC;AACQ;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAEC;AAAc;AAAiB;AAAmB;AAChD;AACM;AACG;AACH;AACK;AACD;AACC;AACe;AAMnC;AAGN;AAQA;AAAuB;AAEnB;AACA;AACA;AAEA;AACG;AACM;AACD;AACC;AACK;AAGZ;AAGN;AAQM;AAIJ;AACA;AACA;AACA;AAEA;AAAoB;AAEhB;AAEA;AACE;AAAY;AACd;AACF;AACqB;AAGvB;AACG;AACM;AACD;AACK;AACJ;AACK;AAKhB;AAUA;AAAoC;AAKhC;AACE;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AAAyD;AACvD;AACA;AACA;AACA;AACU;AAGZ;AACG;AACK;AAC6B;AAC7B;AACC;AACP;AAGN;AAEA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;;;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../src/primitives/Composer/index.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n DetectOverflowOptions,\n UseFloatingOptions,\n} from \"@floating-ui/react-dom\";\nimport {\n autoUpdate,\n flip,\n hide,\n limitShift,\n shift,\n size,\n useFloating,\n} from \"@floating-ui/react-dom\";\nimport type { CommentAttachment, CommentBody } from \"@liveblocks/core\";\nimport { useRoom } from \"@liveblocks/react\";\nimport { useMentionSuggestions } from \"@liveblocks/react/_private\";\nimport { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport type {\n AriaAttributes,\n ChangeEvent,\n FocusEvent,\n FormEvent,\n KeyboardEvent,\n MouseEvent,\n PointerEvent,\n SyntheticEvent,\n} from \"react\";\nimport React, {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport type {\n Descendant as SlateDescendant,\n Element as SlateElement,\n} from \"slate\";\nimport {\n createEditor,\n Editor as SlateEditor,\n insertText as insertSlateText,\n Transforms as SlateTransforms,\n} from \"slate\";\nimport { withHistory } from \"slate-history\";\nimport type {\n RenderElementProps,\n RenderElementSpecificProps,\n RenderLeafProps,\n RenderPlaceholderProps,\n} from \"slate-react\";\nimport {\n Editable,\n ReactEditor,\n Slate,\n useSelected,\n useSlateStatic,\n withReact,\n} from \"slate-react\";\n\nimport { useLiveblocksUIConfig } from \"../../config\";\nimport { FLOATING_ELEMENT_COLLISION_PADDING } from \"../../constants\";\nimport { withAutoFormatting } from \"../../slate/plugins/auto-formatting\";\nimport { withAutoLinks } from \"../../slate/plugins/auto-links\";\nimport { withCustomLinks } from \"../../slate/plugins/custom-links\";\nimport { withEmptyClearFormatting } from \"../../slate/plugins/empty-clear-formatting\";\nimport type { MentionDraft } from \"../../slate/plugins/mentions\";\nimport {\n getMentionDraftAtSelection,\n insertMention,\n insertMentionCharacter,\n MENTION_CHARACTER,\n withMentions,\n} from \"../../slate/plugins/mentions\";\nimport { withNormalize } from \"../../slate/plugins/normalize\";\nimport { withPaste } from \"../../slate/plugins/paste\";\nimport { getDOMRange } from \"../../slate/utils/get-dom-range\";\nimport { isEmpty as isEditorEmpty } from \"../../slate/utils/is-empty\";\nimport { leaveMarkEdge, toggleMark } from \"../../slate/utils/marks\";\nimport type {\n ComposerBody as ComposerBodyData,\n ComposerBodyAutoLink,\n ComposerBodyCustomLink,\n ComposerBodyMention,\n} from \"../../types\";\nimport { isKey } from \"../../utils/is-key\";\nimport { Persist, useAnimationPersist, usePersist } from \"../../utils/Persist\";\nimport { Portal } from \"../../utils/Portal\";\nimport { requestSubmit } from \"../../utils/request-submit\";\nimport { useId } from \"../../utils/use-id\";\nimport { useIndex } from \"../../utils/use-index\";\nimport { useInitial } from \"../../utils/use-initial\";\nimport { useLayoutEffect } from \"../../utils/use-layout-effect\";\nimport { useRefs } from \"../../utils/use-refs\";\nimport { toAbsoluteUrl } from \"../Comment/utils\";\nimport {\n ComposerAttachmentsContext,\n ComposerContext,\n ComposerEditorContext,\n ComposerSuggestionsContext,\n useComposer,\n useComposerAttachmentsContext,\n useComposerEditorContext,\n useComposerSuggestionsContext,\n} from \"./contexts\";\nimport type {\n ComposerAttachFilesProps,\n ComposerAttachmentsDropAreaProps,\n ComposerEditorComponents,\n ComposerEditorElementProps,\n ComposerEditorLinkWrapperProps,\n ComposerEditorMentionSuggestionsWrapperProps,\n ComposerEditorMentionWrapperProps,\n ComposerEditorProps,\n ComposerFormProps,\n ComposerLinkProps,\n ComposerMentionProps,\n ComposerSubmitProps,\n ComposerSuggestionsListItemProps,\n ComposerSuggestionsListProps,\n ComposerSuggestionsProps,\n SuggestionsPosition,\n} from \"./types\";\nimport {\n commentBodyToComposerBody,\n composerBodyToCommentBody,\n getPlacementFromPosition,\n getSideAndAlignFromPlacement,\n useComposerAttachmentsDropArea,\n useComposerAttachmentsManager,\n} from \"./utils\";\n\nconst MENTION_SUGGESTIONS_POSITION: SuggestionsPosition = \"top\";\n\nconst COMPOSER_MENTION_NAME = \"ComposerMention\";\nconst COMPOSER_LINK_NAME = \"ComposerLink\";\nconst COMPOSER_SUGGESTIONS_NAME = \"ComposerSuggestions\";\nconst COMPOSER_SUGGESTIONS_LIST_NAME = \"ComposerSuggestionsList\";\nconst COMPOSER_SUGGESTIONS_LIST_ITEM_NAME = \"ComposerSuggestionsListItem\";\nconst COMPOSER_SUBMIT_NAME = \"ComposerSubmit\";\nconst COMPOSER_EDITOR_NAME = \"ComposerEditor\";\nconst COMPOSER_ATTACH_FILES_NAME = \"ComposerAttachFiles\";\nconst COMPOSER_ATTACHMENTS_DROP_AREA_NAME = \"ComposerAttachmentsDropArea\";\nconst COMPOSER_FORM_NAME = \"ComposerForm\";\n\nconst emptyCommentBody: CommentBody = {\n version: 1,\n content: [{ type: \"paragraph\", children: [{ text: \"\" }] }],\n};\n\nfunction createComposerEditor({\n createAttachments,\n pasteFilesAsAttachments,\n}: {\n createAttachments: (files: File[]) => void;\n pasteFilesAsAttachments?: boolean;\n}) {\n return withNormalize(\n withMentions(\n withCustomLinks(\n withAutoLinks(\n withAutoFormatting(\n withEmptyClearFormatting(\n withPaste(withHistory(withReact(createEditor())), {\n createAttachments,\n pasteFilesAsAttachments,\n })\n )\n )\n )\n )\n )\n );\n}\n\nfunction ComposerEditorMentionWrapper({\n Mention,\n attributes,\n children,\n element,\n}: ComposerEditorMentionWrapperProps) {\n const isSelected = useSelected();\n\n return (\n <span {...attributes}>\n {element.id ? (\n <Mention userId={element.id} isSelected={isSelected} />\n ) : null}\n {children}\n </span>\n );\n}\n\nfunction ComposerEditorLinkWrapper({\n Link,\n attributes,\n element,\n children,\n}: ComposerEditorLinkWrapperProps) {\n const href = useMemo(\n () => toAbsoluteUrl(element.url) ?? element.url,\n [element.url]\n );\n\n return (\n <span {...attributes}>\n <Link href={href}>{children}</Link>\n </span>\n );\n}\n\nfunction ComposerEditorMentionSuggestionsWrapper({\n id,\n itemId,\n userIds,\n selectedUserId,\n setSelectedUserId,\n mentionDraft,\n onItemSelect,\n position = MENTION_SUGGESTIONS_POSITION,\n dir,\n MentionSuggestions,\n}: ComposerEditorMentionSuggestionsWrapperProps) {\n const editor = useSlateStatic();\n const { isFocused } = useComposer();\n const [content, setContent] = useState<HTMLDivElement | null>(null);\n const [contentZIndex, setContentZIndex] = useState<string>();\n const contentRef = useCallback(setContent, [setContent]);\n const { portalContainer } = useLiveblocksUIConfig();\n const floatingOptions: UseFloatingOptions = useMemo(() => {\n const detectOverflowOptions: DetectOverflowOptions = {\n padding: FLOATING_ELEMENT_COLLISION_PADDING,\n };\n\n return {\n strategy: \"fixed\",\n placement: getPlacementFromPosition(position, dir),\n middleware: [\n flip({ ...detectOverflowOptions, crossAxis: false }),\n hide(detectOverflowOptions),\n shift({\n ...detectOverflowOptions,\n limiter: limitShift(),\n }),\n size({\n ...detectOverflowOptions,\n apply({ availableWidth, availableHeight, elements }) {\n elements.floating.style.setProperty(\n \"--lb-composer-suggestions-available-width\",\n `${availableWidth}px`\n );\n elements.floating.style.setProperty(\n \"--lb-composer-suggestions-available-height\",\n `${availableHeight}px`\n );\n },\n }),\n ],\n whileElementsMounted: (...args) => {\n return autoUpdate(...args, {\n animationFrame: true,\n });\n },\n };\n }, [position, dir]);\n const {\n refs: { setReference, setFloating },\n strategy,\n isPositioned,\n placement,\n x,\n y,\n } = useFloating(floatingOptions);\n\n // Copy `z-index` from content to wrapper.\n // Inspired by https://github.com/radix-ui/primitives/blob/main/packages/react/popper/src/Popper.tsx\n useLayoutEffect(() => {\n if (content) {\n setContentZIndex(window.getComputedStyle(content).zIndex);\n }\n }, [content]);\n\n useLayoutEffect(() => {\n if (!mentionDraft) {\n return;\n }\n\n const domRange = getDOMRange(editor, mentionDraft.range);\n\n if (domRange) {\n setReference({\n getBoundingClientRect: () => domRange.getBoundingClientRect(),\n getClientRects: () => domRange.getClientRects(),\n });\n }\n }, [setReference, editor, mentionDraft]);\n\n return (\n <Persist>\n {mentionDraft?.range && isFocused && userIds ? (\n <ComposerSuggestionsContext.Provider\n value={{\n id,\n itemId,\n selectedValue: selectedUserId,\n setSelectedValue: setSelectedUserId,\n onItemSelect,\n placement,\n dir,\n ref: contentRef,\n }}\n >\n <Portal\n ref={setFloating}\n container={portalContainer}\n style={{\n position: strategy,\n top: 0,\n left: 0,\n transform: isPositioned\n ? `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`\n : \"translate3d(0, -200%, 0)\",\n minWidth: \"max-content\",\n zIndex: contentZIndex,\n }}\n >\n <MentionSuggestions\n userIds={userIds}\n selectedUserId={selectedUserId}\n />\n </Portal>\n </ComposerSuggestionsContext.Provider>\n ) : null}\n </Persist>\n );\n}\n\nfunction ComposerEditorElement({\n Mention,\n Link,\n ...props\n}: ComposerEditorElementProps) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n const { attributes, children, element } = props;\n\n switch (element.type) {\n case \"mention\":\n return (\n <ComposerEditorMentionWrapper\n Mention={Mention}\n {...(props as RenderElementSpecificProps<ComposerBodyMention>)}\n />\n );\n case \"auto-link\":\n case \"custom-link\":\n return (\n <ComposerEditorLinkWrapper\n Link={Link}\n {...(props as RenderElementSpecificProps<\n ComposerBodyAutoLink | ComposerBodyCustomLink\n >)}\n />\n );\n case \"paragraph\":\n return (\n <p {...attributes} style={{ position: \"relative\" }}>\n {children}\n </p>\n );\n default:\n return null;\n }\n}\n\n// <code><s><em><strong>text</strong></s></em></code>\nfunction ComposerEditorLeaf({ attributes, children, leaf }: RenderLeafProps) {\n if (leaf.bold) {\n children = <strong>{children}</strong>;\n }\n\n if (leaf.italic) {\n children = <em>{children}</em>;\n }\n\n if (leaf.strikethrough) {\n children = <s>{children}</s>;\n }\n\n if (leaf.code) {\n children = <code>{children}</code>;\n }\n\n return <span {...attributes}>{children}</span>;\n}\n\nfunction ComposerEditorPlaceholder({\n attributes,\n children,\n}: RenderPlaceholderProps) {\n const { opacity: _opacity, ...style } = attributes.style;\n\n return (\n <span {...attributes} style={style} data-placeholder=\"\">\n {children}\n </span>\n );\n}\n\n/**\n * Displays mentions within `Composer.Editor`.\n *\n * @example\n * <Composer.Mention>@{userId}</Composer.Mention>\n */\nconst ComposerMention = forwardRef<HTMLSpanElement, ComposerMentionProps>(\n ({ children, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"span\";\n const isSelected = useSelected();\n\n return (\n <Component\n data-selected={isSelected || undefined}\n {...props}\n ref={forwardedRef}\n >\n {children}\n </Component>\n );\n }\n);\n\n/**\n * Displays links within `Composer.Editor`.\n *\n * @example\n * <Composer.Link href={href}>{children}</Composer.Link>\n */\nconst ComposerLink = forwardRef<HTMLAnchorElement, ComposerLinkProps>(\n ({ children, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"a\";\n\n return (\n <Component\n target=\"_blank\"\n rel=\"noopener noreferrer nofollow\"\n {...props}\n ref={forwardedRef}\n >\n {children}\n </Component>\n );\n }\n);\n\n/**\n * Contains suggestions within `Composer.Editor`.\n */\nconst ComposerSuggestions = forwardRef<\n HTMLDivElement,\n ComposerSuggestionsProps\n>(({ children, style, asChild, ...props }, forwardedRef) => {\n const [isPresent] = usePersist();\n const ref = useRef<HTMLDivElement>(null);\n const {\n ref: contentRef,\n placement,\n dir,\n } = useComposerSuggestionsContext(COMPOSER_SUGGESTIONS_NAME);\n const mergedRefs = useRefs(forwardedRef, contentRef, ref);\n const [side, align] = useMemo(\n () => getSideAndAlignFromPlacement(placement),\n [placement]\n );\n const Component = asChild ? Slot : \"div\";\n useAnimationPersist(ref);\n\n return (\n <Component\n dir={dir}\n {...props}\n data-state={isPresent ? \"open\" : \"closed\"}\n data-side={side}\n data-align={align}\n style={{\n display: \"flex\",\n flexDirection: \"column\",\n maxHeight: \"var(--lb-composer-suggestions-available-height)\",\n overflowY: \"auto\",\n ...style,\n }}\n ref={mergedRefs}\n >\n {children}\n </Component>\n );\n});\n\n/**\n * Displays a list of suggestions within `Composer.Editor`.\n *\n * @example\n * <Composer.SuggestionsList>\n * {userIds.map((userId) => (\n * <Composer.SuggestionsListItem key={userId} value={userId}>\n * @{userId}\n * </Composer.SuggestionsListItem>\n * ))}\n * </Composer.SuggestionsList>\n */\nconst ComposerSuggestionsList = forwardRef<\n HTMLUListElement,\n ComposerSuggestionsListProps\n>(({ children, asChild, ...props }, forwardedRef) => {\n const { id } = useComposerSuggestionsContext(COMPOSER_SUGGESTIONS_LIST_NAME);\n const Component = asChild ? Slot : \"ul\";\n\n return (\n <Component\n role=\"listbox\"\n id={id}\n aria-label=\"Suggestions list\"\n {...props}\n ref={forwardedRef}\n >\n {children}\n </Component>\n );\n});\n\n/**\n * Displays a suggestion within `Composer.SuggestionsList`.\n *\n * @example\n * <Composer.SuggestionsListItem key={userId} value={userId}>\n * @{userId}\n * </Composer.SuggestionsListItem>\n */\nconst ComposerSuggestionsListItem = forwardRef<\n HTMLLIElement,\n ComposerSuggestionsListItemProps\n>(\n (\n {\n value,\n children,\n onPointerMove,\n onPointerDown,\n onClick,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const ref = useRef<HTMLLIElement>(null);\n const mergedRefs = useRefs(forwardedRef, ref);\n const { selectedValue, setSelectedValue, itemId, onItemSelect } =\n useComposerSuggestionsContext(COMPOSER_SUGGESTIONS_LIST_ITEM_NAME);\n const Component = asChild ? Slot : \"li\";\n const isSelected = useMemo(\n () => selectedValue === value,\n [selectedValue, value]\n );\n // TODO: Support props.id if provided, it will need to be sent up to Composer.Editor to use it in aria-activedescendant\n const id = useMemo(() => itemId(value), [itemId, value]);\n\n useEffect(() => {\n if (ref?.current && isSelected) {\n ref.current.scrollIntoView({ block: \"nearest\" });\n }\n }, [isSelected]);\n\n const handlePointerMove = useCallback(\n (event: PointerEvent<HTMLLIElement>) => {\n onPointerMove?.(event);\n\n if (!event.isDefaultPrevented()) {\n setSelectedValue(value);\n }\n },\n [onPointerMove, setSelectedValue, value]\n );\n\n const handlePointerDown = useCallback(\n (event: PointerEvent<HTMLLIElement>) => {\n onPointerDown?.(event);\n\n event.preventDefault();\n event.stopPropagation();\n },\n [onPointerDown]\n );\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLLIElement>) => {\n onClick?.(event);\n\n const wasDefaultPrevented = event.isDefaultPrevented();\n\n event.preventDefault();\n event.stopPropagation();\n\n if (!wasDefaultPrevented) {\n onItemSelect(value);\n }\n },\n [onClick, onItemSelect, value]\n );\n\n return (\n <Component\n role=\"option\"\n id={id}\n data-selected={isSelected || undefined}\n aria-selected={isSelected || undefined}\n onPointerMove={handlePointerMove}\n onPointerDown={handlePointerDown}\n onClick={handleClick}\n {...props}\n ref={mergedRefs}\n >\n {children}\n </Component>\n );\n }\n);\n\nconst defaultEditorComponents: ComposerEditorComponents = {\n Link: ({ href, children }) => {\n return <ComposerLink href={href}>{children}</ComposerLink>;\n },\n Mention: ({ userId }) => {\n return (\n <ComposerMention>\n {MENTION_CHARACTER}\n {userId}\n </ComposerMention>\n );\n },\n MentionSuggestions: ({ userIds }) => {\n return userIds.length > 0 ? (\n <ComposerSuggestions>\n <ComposerSuggestionsList>\n {userIds.map((userId) => (\n <ComposerSuggestionsListItem key={userId} value={userId}>\n {userId}\n </ComposerSuggestionsListItem>\n ))}\n </ComposerSuggestionsList>\n </ComposerSuggestions>\n ) : null;\n },\n};\n\n/**\n * Displays the composer's editor.\n *\n * @example\n * <Composer.Editor placeholder=\"Write a comment…\" />\n */\nconst ComposerEditor = forwardRef<HTMLDivElement, ComposerEditorProps>(\n (\n {\n defaultValue,\n onKeyDown,\n onFocus,\n onBlur,\n disabled,\n autoFocus,\n components,\n dir,\n ...props\n },\n forwardedRef\n ) => {\n const { editor, validate, setFocused } = useComposerEditorContext();\n const {\n submit,\n focus,\n select,\n canSubmit,\n isDisabled: isComposerDisabled,\n isFocused,\n } = useComposer();\n const isDisabled = isComposerDisabled || disabled;\n const initialBody = useInitial(defaultValue ?? emptyCommentBody);\n const initialEditorValue = useMemo(() => {\n return commentBodyToComposerBody(initialBody);\n }, [initialBody]);\n const { Link, Mention, MentionSuggestions } = useMemo(\n () => ({ ...defaultEditorComponents, ...components }),\n [components]\n );\n\n const [mentionDraft, setMentionDraft] = useState<MentionDraft>();\n const mentionSuggestions = useMentionSuggestions(mentionDraft?.text);\n const [\n selectedMentionSuggestionIndex,\n setPreviousSelectedMentionSuggestionIndex,\n setNextSelectedMentionSuggestionIndex,\n setSelectedMentionSuggestionIndex,\n ] = useIndex(0, mentionSuggestions?.length ?? 0);\n const id = useId();\n const suggestionsListId = useMemo(\n () => `liveblocks-suggestions-list-${id}`,\n [id]\n );\n const suggestionsListItemId = useCallback(\n (userId?: string) =>\n userId ? `liveblocks-suggestions-list-item-${id}-${userId}` : undefined,\n [id]\n );\n const renderElement = useCallback(\n (props: RenderElementProps) => {\n return (\n <ComposerEditorElement Mention={Mention} Link={Link} {...props} />\n );\n },\n [Link, Mention]\n );\n\n const handleChange = useCallback(\n (value: SlateDescendant[]) => {\n validate(value as SlateElement[]);\n\n setMentionDraft(getMentionDraftAtSelection(editor));\n },\n [editor, validate]\n );\n\n const createMention = useCallback(\n (userId?: string) => {\n if (!mentionDraft || !userId) {\n return;\n }\n\n SlateTransforms.select(editor, mentionDraft.range);\n insertMention(editor, userId);\n setMentionDraft(undefined);\n setSelectedMentionSuggestionIndex(0);\n },\n [editor, mentionDraft, setSelectedMentionSuggestionIndex]\n );\n\n const handleKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n onKeyDown?.(event);\n\n if (event.isDefaultPrevented()) {\n return;\n }\n\n // Allow leaving marks with ArrowLeft\n if (isKey(event, \"ArrowLeft\")) {\n leaveMarkEdge(editor, \"start\");\n }\n\n // Allow leaving marks with ArrowRight\n if (isKey(event, \"ArrowRight\")) {\n leaveMarkEdge(editor, \"end\");\n }\n\n if (mentionDraft && mentionSuggestions?.length) {\n // Select the next mention suggestion on ArrowDown\n if (isKey(event, \"ArrowDown\")) {\n event.preventDefault();\n setNextSelectedMentionSuggestionIndex();\n }\n\n // Select the previous mention suggestion on ArrowUp\n if (isKey(event, \"ArrowUp\")) {\n event.preventDefault();\n setPreviousSelectedMentionSuggestionIndex();\n }\n\n // Create a mention on Enter/Tab\n if (isKey(event, \"Enter\") || isKey(event, \"Tab\")) {\n event.preventDefault();\n\n const userId = mentionSuggestions?.[selectedMentionSuggestionIndex];\n createMention(userId);\n }\n\n // Close the suggestions on Escape\n if (isKey(event, \"Escape\")) {\n event.preventDefault();\n setMentionDraft(undefined);\n setSelectedMentionSuggestionIndex(0);\n }\n } else {\n // Blur the editor on Escape\n if (isKey(event, \"Escape\")) {\n event.preventDefault();\n ReactEditor.blur(editor);\n }\n\n // Submit the editor on Enter\n if (isKey(event, \"Enter\", { shift: false })) {\n // Even if submitting is not possible, don't do anything else on Enter. (e.g. creating a new line)\n event.preventDefault();\n\n if (canSubmit) {\n submit();\n }\n }\n\n // Create a new line on Shift + Enter\n if (isKey(event, \"Enter\", { shift: true })) {\n event.preventDefault();\n editor.insertBreak();\n }\n\n // Toggle bold on Command/Control + B\n if (isKey(event, \"b\", { mod: true })) {\n event.preventDefault();\n toggleMark(editor, \"bold\");\n }\n\n // Toggle italic on Command/Control + I\n if (isKey(event, \"i\", { mod: true })) {\n event.preventDefault();\n toggleMark(editor, \"italic\");\n }\n\n // Toggle strikethrough on Command/Control + Shift + S\n if (isKey(event, \"s\", { mod: true, shift: true })) {\n event.preventDefault();\n toggleMark(editor, \"strikethrough\");\n }\n\n // Toggle code on Command/Control + E\n if (isKey(event, \"e\", { mod: true })) {\n event.preventDefault();\n toggleMark(editor, \"code\");\n }\n }\n },\n [\n createMention,\n editor,\n canSubmit,\n mentionDraft,\n mentionSuggestions,\n selectedMentionSuggestionIndex,\n onKeyDown,\n setNextSelectedMentionSuggestionIndex,\n setPreviousSelectedMentionSuggestionIndex,\n setSelectedMentionSuggestionIndex,\n submit,\n ]\n );\n\n const handleFocus = useCallback(\n (event: FocusEvent<HTMLDivElement>) => {\n onFocus?.(event);\n\n if (!event.isDefaultPrevented()) {\n setFocused(true);\n }\n },\n [onFocus, setFocused]\n );\n\n const handleBlur = useCallback(\n (event: FocusEvent<HTMLDivElement>) => {\n onBlur?.(event);\n\n if (!event.isDefaultPrevented()) {\n setFocused(false);\n }\n },\n [onBlur, setFocused]\n );\n\n const selectedMentionSuggestionUserId = useMemo(\n () => mentionSuggestions?.[selectedMentionSuggestionIndex],\n [selectedMentionSuggestionIndex, mentionSuggestions]\n );\n const setSelectedMentionSuggestionUserId = useCallback(\n (userId: string) => {\n const index = mentionSuggestions?.indexOf(userId);\n\n if (index !== undefined && index >= 0) {\n setSelectedMentionSuggestionIndex(index);\n }\n },\n [setSelectedMentionSuggestionIndex, mentionSuggestions]\n );\n\n const propsWhileSuggesting: AriaAttributes = useMemo(\n () =>\n mentionDraft\n ? {\n role: \"combobox\",\n \"aria-autocomplete\": \"list\",\n \"aria-expanded\": true,\n \"aria-controls\": suggestionsListId,\n \"aria-activedescendant\": suggestionsListItemId(\n selectedMentionSuggestionUserId\n ),\n }\n : {},\n [\n mentionDraft,\n suggestionsListId,\n suggestionsListItemId,\n selectedMentionSuggestionUserId,\n ]\n );\n\n useImperativeHandle(forwardedRef, () => {\n return ReactEditor.toDOMNode(editor, editor) as HTMLDivElement;\n }, [editor]);\n\n // Manually focus the editor when `autoFocus` is true\n useEffect(() => {\n if (autoFocus) {\n focus();\n }\n }, [autoFocus, editor, focus]);\n\n // Manually add a selection in the editor if the selection\n // is still empty after being focused\n useEffect(() => {\n if (isFocused && editor.selection === null) {\n select();\n }\n }, [editor, select, isFocused]);\n\n return (\n <Slate\n editor={editor}\n initialValue={initialEditorValue}\n onChange={handleChange}\n >\n <Editable\n dir={dir}\n enterKeyHint={mentionDraft ? \"enter\" : \"send\"}\n autoCapitalize=\"sentences\"\n aria-label=\"Composer editor\"\n data-focused={isFocused || undefined}\n data-disabled={isDisabled || undefined}\n {...propsWhileSuggesting}\n {...props}\n readOnly={isDisabled}\n disabled={isDisabled}\n onKeyDown={handleKeyDown}\n onFocus={handleFocus}\n onBlur={handleBlur}\n renderElement={renderElement}\n renderLeaf={ComposerEditorLeaf}\n renderPlaceholder={ComposerEditorPlaceholder}\n />\n <ComposerEditorMentionSuggestionsWrapper\n dir={dir}\n mentionDraft={mentionDraft}\n selectedUserId={selectedMentionSuggestionUserId}\n setSelectedUserId={setSelectedMentionSuggestionUserId}\n userIds={mentionSuggestions}\n id={suggestionsListId}\n itemId={suggestionsListItemId}\n onItemSelect={createMention}\n MentionSuggestions={MentionSuggestions}\n />\n </Slate>\n );\n }\n);\n\nconst MAX_ATTACHMENTS = 10;\nconst MAX_ATTACHMENT_SIZE = 1024 * 1024 * 1024; // 1 GB\n\n/**\n * Surrounds the composer's content and handles submissions.\n *\n * @example\n * <Composer.Form onComposerSubmit={({ body }) => {}}>\n *\t <Composer.Editor />\n * <Composer.Submit />\n * </Composer.Form>\n */\nconst ComposerForm = forwardRef<HTMLFormElement, ComposerFormProps>(\n (\n {\n children,\n onSubmit,\n onComposerSubmit,\n defaultAttachments = [],\n pasteFilesAsAttachments,\n disabled,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"form\";\n const room = useRoom();\n const [isEmpty, setEmpty] = useState(true);\n const [isSubmitting, setSubmitting] = useState(false);\n const [isFocused, setFocused] = useState(false);\n // Later: Offer as Composer.Form props: { maxAttachments: number; maxAttachmentSize: number; supportedAttachmentMimeTypes: string[]; }\n const maxAttachments = MAX_ATTACHMENTS;\n const maxAttachmentSize = MAX_ATTACHMENT_SIZE;\n const {\n attachments,\n isUploadingAttachments,\n addAttachments,\n removeAttachment,\n clearAttachments,\n } = useComposerAttachmentsManager(defaultAttachments, {\n maxFileSize: maxAttachmentSize,\n });\n const numberOfAttachments = attachments.length;\n const hasMaxAttachments = numberOfAttachments >= maxAttachments;\n const isDisabled = useMemo(() => {\n const self = room.getSelf();\n const canComment = self?.canComment ?? true;\n\n return isSubmitting || disabled || !canComment;\n }, [isSubmitting, disabled, room]);\n const canSubmit = useMemo(() => {\n return !isEmpty && !isUploadingAttachments;\n }, [isEmpty, isUploadingAttachments]);\n const ref = useRef<HTMLFormElement>(null);\n const mergedRefs = useRefs(forwardedRef, ref);\n const fileInputRef = useRef<HTMLInputElement>(null);\n\n const createAttachments = useCallback(\n (files: File[]) => {\n if (!files.length) {\n return;\n }\n\n const numberOfAcceptedFiles = Math.max(\n 0,\n maxAttachments - numberOfAttachments\n );\n\n files.splice(numberOfAcceptedFiles);\n\n const attachments = files.map((file) => room.prepareAttachment(file));\n\n addAttachments(attachments);\n },\n [addAttachments, maxAttachments, numberOfAttachments, room]\n );\n\n const createAttachmentsRef = useRef(createAttachments);\n\n useEffect(() => {\n createAttachmentsRef.current = createAttachments;\n }, [createAttachments]);\n\n const stableCreateAttachments = useCallback((files: File[]) => {\n createAttachmentsRef.current(files);\n }, []);\n\n const editor = useInitial(() =>\n createComposerEditor({\n createAttachments: stableCreateAttachments,\n pasteFilesAsAttachments,\n })\n );\n\n const validate = useCallback(\n (value: SlateElement[]) => {\n setEmpty(isEditorEmpty(editor, value));\n },\n [editor]\n );\n\n const submit = useCallback(() => {\n if (!canSubmit) {\n return;\n }\n\n // We need to wait for the next frame in some cases like when composing diacritics,\n // we want any native handling to be done first while still being handled on `keydown`.\n requestAnimationFrame(() => {\n if (ref.current) {\n requestSubmit(ref.current);\n }\n });\n }, [canSubmit]);\n\n const clear = useCallback(() => {\n SlateTransforms.delete(editor, {\n at: {\n anchor: SlateEditor.start(editor, []),\n focus: SlateEditor.end(editor, []),\n },\n });\n }, [editor]);\n\n const select = useCallback(() => {\n SlateTransforms.select(editor, {\n anchor: SlateEditor.end(editor, []),\n focus: SlateEditor.end(editor, []),\n });\n }, [editor]);\n\n const focus = useCallback(\n (resetSelection = true) => {\n if (!ReactEditor.isFocused(editor)) {\n SlateTransforms.select(\n editor,\n resetSelection || !editor.selection\n ? SlateEditor.end(editor, [])\n : editor.selection\n );\n ReactEditor.focus(editor);\n }\n },\n [editor]\n );\n\n const blur = useCallback(() => {\n ReactEditor.blur(editor);\n }, [editor]);\n\n const createMention = useCallback(() => {\n if (disabled) {\n return;\n }\n\n focus();\n insertMentionCharacter(editor);\n }, [disabled, editor, focus]);\n\n const insertText = useCallback(\n (text: string) => {\n if (disabled) {\n return;\n }\n\n focus(false);\n insertSlateText(editor, text);\n },\n [disabled, editor, focus]\n );\n\n const attachFiles = useCallback(() => {\n if (disabled) {\n return;\n }\n\n if (fileInputRef.current) {\n fileInputRef.current.click();\n }\n }, [disabled]);\n\n const handleAttachmentsInputChange = useCallback(\n (event: ChangeEvent<HTMLInputElement>) => {\n if (disabled) {\n return;\n }\n\n if (event.target.files) {\n createAttachments(Array.from(event.target.files));\n\n // Reset the input value to allow selecting the same file(s) again\n event.target.value = \"\";\n }\n },\n [createAttachments, disabled]\n );\n\n const onSubmitEnd = useCallback(() => {\n clear();\n blur();\n clearAttachments();\n setSubmitting(false);\n }, [blur, clear, clearAttachments]);\n\n const handleSubmit = useCallback(\n (event: FormEvent<HTMLFormElement>) => {\n if (disabled) {\n return;\n }\n\n // In some situations (e.g. pressing Enter while composing diacritics), it's possible\n // for the form to be submitted as empty even though we already checked whether the\n // editor was empty when handling the key press.\n const isEmpty = isEditorEmpty(editor, editor.children);\n\n // We even prevent the user's `onSubmit` handler from being called if the editor is empty.\n if (isEmpty) {\n event.preventDefault();\n\n return;\n }\n\n onSubmit?.(event);\n\n if (!onComposerSubmit || event.isDefaultPrevented()) {\n event.preventDefault();\n\n return;\n }\n\n const body = composerBodyToCommentBody(\n editor.children as ComposerBodyData\n );\n // Only non-local attachments are included to be submitted.\n const commentAttachments: CommentAttachment[] = attachments\n .filter(\n (attachment) =>\n attachment.type === \"attachment\" ||\n (attachment.type === \"localAttachment\" &&\n attachment.status === \"uploaded\")\n )\n .map((attachment) => {\n return {\n id: attachment.id,\n type: \"attachment\",\n mimeType: attachment.mimeType,\n size: attachment.size,\n name: attachment.name,\n };\n });\n\n const promise = onComposerSubmit(\n { body, attachments: commentAttachments },\n event\n );\n\n event.preventDefault();\n\n if (promise) {\n setSubmitting(true);\n promise.then(onSubmitEnd);\n } else {\n onSubmitEnd();\n }\n },\n [disabled, editor, attachments, onComposerSubmit, onSubmit, onSubmitEnd]\n );\n\n const stopPropagation = useCallback((event: SyntheticEvent) => {\n event.stopPropagation();\n }, []);\n\n return (\n <ComposerEditorContext.Provider\n value={{\n editor,\n validate,\n setFocused,\n }}\n >\n <ComposerAttachmentsContext.Provider\n value={{\n createAttachments,\n isUploadingAttachments,\n hasMaxAttachments,\n maxAttachments,\n maxAttachmentSize,\n }}\n >\n <ComposerContext.Provider\n value={{\n isDisabled,\n isFocused,\n isEmpty,\n canSubmit,\n submit,\n clear,\n select,\n focus,\n blur,\n createMention,\n insertText,\n attachments,\n attachFiles,\n removeAttachment,\n }}\n >\n <Component {...props} onSubmit={handleSubmit} ref={mergedRefs}>\n <input\n type=\"file\"\n multiple\n ref={fileInputRef}\n onChange={handleAttachmentsInputChange}\n onClick={stopPropagation}\n tabIndex={-1}\n style={{ display: \"none\" }}\n />\n <Slottable>{children}</Slottable>\n </Component>\n </ComposerContext.Provider>\n </ComposerAttachmentsContext.Provider>\n </ComposerEditorContext.Provider>\n );\n }\n);\n\n/**\n * A button to submit the composer.\n *\n * @example\n * <Composer.Submit>Send</Composer.Submit>\n */\nconst ComposerSubmit = forwardRef<HTMLButtonElement, ComposerSubmitProps>(\n ({ children, disabled, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"button\";\n const { canSubmit, isDisabled: isComposerDisabled } = useComposer();\n const isDisabled = isComposerDisabled || disabled || !canSubmit;\n\n return (\n <Component\n type=\"submit\"\n {...props}\n ref={forwardedRef}\n disabled={isDisabled}\n >\n {children}\n </Component>\n );\n }\n);\n\n/**\n * A button which opens a file picker to create attachments.\n *\n * @example\n * <Composer.AttachFiles>Attach files</Composer.AttachFiles>\n */\nconst ComposerAttachFiles = forwardRef<\n HTMLButtonElement,\n ComposerAttachFilesProps\n>(({ children, onClick, disabled, asChild, ...props }, forwardedRef) => {\n const Component = asChild ? Slot : \"button\";\n const { hasMaxAttachments } = useComposerAttachmentsContext();\n const { isDisabled: isComposerDisabled, attachFiles } = useComposer();\n const isDisabled = isComposerDisabled || hasMaxAttachments || disabled;\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n attachFiles();\n }\n },\n [attachFiles, onClick]\n );\n\n return (\n <Component\n type=\"button\"\n {...props}\n onClick={handleClick}\n ref={forwardedRef}\n disabled={isDisabled}\n >\n {children}\n </Component>\n );\n});\n\n/**\n * A drop area which accepts files to create attachments.\n *\n * @example\n * <Composer.AttachmentsDropArea>\n * Drop files here\n * </Composer.AttachmentsDropArea>\n */\nconst ComposerAttachmentsDropArea = forwardRef<\n HTMLDivElement,\n ComposerAttachmentsDropAreaProps\n>(\n (\n {\n onDragEnter,\n onDragLeave,\n onDragOver,\n onDrop,\n disabled,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"div\";\n const { isDisabled: isComposerDisabled } = useComposer();\n const isDisabled = isComposerDisabled || disabled;\n const [, dropAreaProps] = useComposerAttachmentsDropArea({\n onDragEnter,\n onDragLeave,\n onDragOver,\n onDrop,\n disabled: isDisabled,\n });\n\n return (\n <Component\n {...dropAreaProps}\n data-disabled={isDisabled ? \"\" : undefined}\n {...props}\n ref={forwardedRef}\n />\n );\n }\n);\n\nif (process.env.NODE_ENV !== \"production\") {\n ComposerAttachFiles.displayName = COMPOSER_ATTACH_FILES_NAME;\n ComposerAttachmentsDropArea.displayName = COMPOSER_ATTACHMENTS_DROP_AREA_NAME;\n ComposerEditor.displayName = COMPOSER_EDITOR_NAME;\n ComposerForm.displayName = COMPOSER_FORM_NAME;\n ComposerMention.displayName = COMPOSER_MENTION_NAME;\n ComposerLink.displayName = COMPOSER_LINK_NAME;\n ComposerSubmit.displayName = COMPOSER_SUBMIT_NAME;\n ComposerSuggestions.displayName = COMPOSER_SUGGESTIONS_NAME;\n ComposerSuggestionsList.displayName = COMPOSER_SUGGESTIONS_LIST_NAME;\n ComposerSuggestionsListItem.displayName = COMPOSER_SUGGESTIONS_LIST_ITEM_NAME;\n}\n\n// NOTE: Every export from this file will be available publicly as Composer.*\nexport {\n ComposerAttachFiles as AttachFiles,\n ComposerAttachmentsDropArea as AttachmentsDropArea,\n ComposerEditor as Editor,\n ComposerForm as Form,\n ComposerLink as Link,\n ComposerMention as Mention,\n ComposerSubmit as Submit,\n ComposerSuggestions as Suggestions,\n ComposerSuggestionsList as SuggestionsList,\n ComposerSuggestionsListItem as SuggestionsListItem,\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwIA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAsC;AAC3B;AAEX;AAEA;AAA8B;AAC5B;AAEF;AAIE;AAAO;AACL;AACE;AACE;AACE;AACE;AACoD;AAChD;AACA;AACD;AACH;AACF;AACF;AACF;AACF;AAEJ;AAEA;AAAsC;AACpC;AACA;AACA;AAEF;AACE;AAEA;AACG;AAAS;AAEL;AAAwB;AAAI;AAKrC;AAEA;AAAmC;AACjC;AACA;AACA;AAEF;AACE;AAAa;AACiC;AAChC;AAGd;AACG;AAAS;AACP;AAAK;AAGZ;AAEA;AAAiD;AAC/C;AACA;AACA;AACA;AACA;AACA;AACA;AACW;AACX;AAEF;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACE;AAAqD;AAC1C;AAGX;AAAO;AACK;AACuC;AACrC;AACyC;AACzB;AACpB;AACD;AACiB;AACrB;AACI;AACA;AAED;AAAwB;AACtB;AACG;AAEL;AAAwB;AACtB;AACG;AACL;AACF;AACD;AACH;AAEE;AAA2B;AACT;AACjB;AACH;AACF;AAEF;AAAM;AAC8B;AAClC;AACA;AACA;AACA;AACA;AAKF;AACE;AACE;AAAwD;AAC1D;AAGF;AACE;AACE;AAAA;AAGF;AAEA;AACE;AAAa;AACiD;AACd;AAC/C;AACH;AAGF;AAGO;AACQ;AACL;AACA;AACe;AACG;AAClB;AACA;AACA;AACK;AACP;AAEC;AACM;AACM;AACJ;AACK;AACL;AACC;AAGF;AACM;AACF;AACV;AAEC;AACC;AACA;AAOd;AAEA;AAA+B;AAC7B;AACA;AAEF;AAEE;AAEA;AAAsB;AAElB;AACG;AACC;AACK;AACP;AAEC;AAEH;AACG;AACC;AACK;AAGP;AAGF;AACG;AAAM;AAA0C;AAEjD;AAGF;AAAO;AAEb;AAGA;AACE;AACE;AAA6B;AAG/B;AACE;AAAyB;AAG3B;AACE;AAAwB;AAG1B;AACE;AAA2B;AAG7B;AAAQ;AAAS;AACnB;AAEA;AAAmC;AACjC;AAEF;AACE;AAEA;AACG;AAAS;AAAY;AAA+B;AAIzD;AAQA;AAAwB;AAEpB;AACA;AAEA;AACG;AAC8B;AACzB;AACC;AAGP;AAGN;AAQA;AAAqB;AAEjB;AAEA;AACG;AACQ;AACH;AACA;AACC;AAGP;AAGN;AAKM;AAIJ;AACA;AACA;AAAM;AACC;AACL;AACA;AAEF;AACA;AAAsB;AACwB;AAClC;AAEZ;AACA;AAEA;AACG;AACC;AACI;AAC6B;AACtB;AACC;AACL;AACI;AACM;AACJ;AACA;AACR;AACL;AACK;AAKX;AAcM;AAIJ;AACA;AAEA;AACG;AACM;AACL;AACW;AACP;AACC;AAKX;AAUA;AAAoC;AAKhC;AACE;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AAEA;AACA;AAAmB;AACO;AACH;AAGvB;AAEA;AACE;AACE;AAA+C;AACjD;AAGF;AAA0B;AAEtB;AAEA;AACE;AAAsB;AACxB;AACF;AACuC;AAGzC;AAA0B;AAEtB;AAEA;AACA;AAAsB;AACxB;AACc;AAGhB;AAAoB;AAEhB;AAEA;AAEA;AACA;AAEA;AACE;AAAkB;AACpB;AACF;AAC6B;AAG/B;AACG;AACM;AACL;AAC6B;AACA;AACd;AACA;AACN;AACL;AACC;AAGP;AAGN;AAEA;AAA0D;AAEtD;AAAQ;AAAa;AAAsB;AAC7C;AAEE;AAIE;AAEJ;AAEE;AAIS;AAAiC;AAAe;AAMrD;AAER;AAQA;AAAuB;AAEnB;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AAAM;AACJ;AACA;AACA;AACA;AACY;AACZ;AAEF;AACA;AACA;AACE;AAA4C;AAE9C;AAA8C;AACO;AACxC;AAGb;AACA;AACA;AAAM;AACJ;AACA;AACA;AACA;AAEF;AACA;AAA0B;AACa;AAClC;AAEL;AAA8B;AAEoC;AAC7D;AAEL;AAAsB;AAElB;AACG;AAAsB;AAAkB;AAAgB;AAAO;AAEpE;AACc;AAGhB;AAAqB;AAEjB;AAEA;AAAkD;AACpD;AACiB;AAGnB;AAAsB;AAElB;AACE;AAAA;AAGF;AACA;AACA;AACA;AAAmC;AACrC;AACwD;AAG1D;AAAsB;AAElB;AAEA;AACE;AAAA;AAIF;AACE;AAA6B;AAI/B;AACE;AAA2B;AAG7B;AAEE;AACE;AACA;AAAsC;AAIxC;AACE;AACA;AAA0C;AAI5C;AACE;AAEA;AACA;AAAoB;AAItB;AACE;AACA;AACA;AAAmC;AACrC;AAGA;AACE;AACA;AAAuB;AAIzB;AAEE;AAEA;AACE;AAAO;AACT;AAIF;AACE;AACA;AAAmB;AAIrB;AACE;AACA;AAAyB;AAI3B;AACE;AACA;AAA2B;AAI7B;AACE;AACA;AAAkC;AAIpC;AACE;AACA;AAAyB;AAC3B;AACF;AACF;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAGF;AAAoB;AAEhB;AAEA;AACE;AAAe;AACjB;AACF;AACoB;AAGtB;AAAmB;AAEf;AAEA;AACE;AAAgB;AAClB;AACF;AACmB;AAGrB;AAAwC;AACX;AACwB;AAErD;AAA2C;AAEvC;AAEA;AACE;AAAuC;AACzC;AACF;AACsD;AAGxD;AAA6C;AAGrC;AACQ;AACe;AACJ;AACA;AACQ;AACvB;AACF;AAED;AACP;AACE;AACA;AACA;AACA;AACF;AAGF;AACE;AAA2C;AAI7C;AACE;AACE;AAAM;AACR;AAKF;AACE;AACE;AAAO;AACT;AAGF;AACG;AACC;AACc;AACJ;AAET;AACC;AACuC;AACxB;AACJ;AACgB;AACE;AACzB;AACA;AACM;AACA;AACC;AACF;AACD;AACR;AACY;AACO;AAEpB;AACC;AACA;AACgB;AACG;AACV;AACL;AACI;AACM;AACd;AAEJ;AAGN;AAEA;AACA;AAWA;AAAqB;AAEjB;AACE;AACA;AACA;AACsB;AACtB;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAM;AACJ;AACA;AACA;AACA;AACA;AACoD;AACvC;AAEf;AACA;AACA;AACE;AACA;AAEA;AAAoC;AAEtC;AACE;AAAoB;AAEtB;AACA;AACA;AAEA;AAA0B;AAEtB;AACE;AAAA;AAGF;AAAmC;AACjC;AACiB;AAGnB;AAEA;AAEA;AAA0B;AAC5B;AAC0D;AAG5D;AAEA;AACE;AAA+B;AAGjC;AACE;AAAkC;AAGpC;AAAe;AACQ;AACA;AACnB;AACD;AAGH;AAAiB;AAEb;AAAqC;AACvC;AACO;AAGT;AACE;AACE;AAAA;AAKF;AACE;AACE;AAAyB;AAC3B;AACD;AAGH;AACE;AAA+B;AACzB;AACkC;AACH;AACnC;AACD;AAGH;AACE;AAA+B;AACK;AACD;AAClC;AAGH;AAAc;AAEV;AACE;AAAgB;AACd;AAGW;AAEb;AAAwB;AAC1B;AACF;AACO;AAGT;AACE;AAAuB;AAGzB;AACE;AACE;AAAA;AAGF;AACA;AAA6B;AAG/B;AAAmB;AAEf;AACE;AAAA;AAGF;AACA;AAA4B;AAC9B;AACwB;AAG1B;AACE;AACE;AAAA;AAGF;AACE;AAA2B;AAC7B;AAGF;AAAqC;AAEjC;AACE;AAAA;AAGF;AACE;AAGA;AAAqB;AACvB;AACF;AAC4B;AAG9B;AACE;AACA;AACA;AACA;AAAmB;AAGrB;AAAqB;AAEjB;AACE;AAAA;AAMF;AAGA;AACE;AAEA;AAAA;AAGF;AAEA;AACE;AAEA;AAAA;AAGF;AAAa;AACJ;AAGT;AACG;AAI2B;AAG1B;AAAO;AACU;AACT;AACe;AACJ;AACA;AACnB;AAGJ;AAAgB;AAC0B;AACxC;AAGF;AAEA;AACE;AACA;AAAwB;AAExB;AAAY;AACd;AACF;AACuE;AAGzE;AACE;AAAsB;AAGxB;AACG;AACQ;AACL;AACA;AACA;AACF;AAEC;AACQ;AACL;AACA;AACA;AACA;AACA;AACF;AAEC;AACQ;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAEC;AAAc;AAAiB;AAAmB;AAChD;AACM;AACG;AACH;AACK;AACD;AACC;AACe;AAMnC;AAGN;AAQA;AAAuB;AAEnB;AACA;AACA;AAEA;AACG;AACM;AACD;AACC;AACK;AAGZ;AAGN;AAQM;AAIJ;AACA;AACA;AACA;AAEA;AAAoB;AAEhB;AAEA;AACE;AAAY;AACd;AACF;AACqB;AAGvB;AACG;AACM;AACD;AACK;AACJ;AACK;AAKhB;AAUA;AAAoC;AAKhC;AACE;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AAAyD;AACvD;AACA;AACA;AACA;AACU;AAGZ;AACG;AACK;AAC6B;AAC7B;AACC;AACP;AAGN;AAEA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;;;;;;;;;;;"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { flip, hide, shift, limitShift, size, autoUpdate, useFloating } from '@floating-ui/react-dom';
|
|
3
3
|
import { useRoom } from '@liveblocks/react';
|
|
4
|
+
import { useMentionSuggestions } from '@liveblocks/react/_private';
|
|
4
5
|
import { Slottable, Slot } from '@radix-ui/react-slot';
|
|
5
6
|
import React__default, { forwardRef, useRef, useMemo, useEffect, useCallback, useState, useImperativeHandle } from 'react';
|
|
6
7
|
import { Transforms, Editor, insertText, createEditor } from 'slate';
|
|
@@ -8,12 +9,12 @@ import { withHistory } from 'slate-history';
|
|
|
8
9
|
import { useSelected, ReactEditor, Slate, Editable, useSlateStatic, withReact } from 'slate-react';
|
|
9
10
|
import { useLiveblocksUIConfig } from '../../config.mjs';
|
|
10
11
|
import { FLOATING_ELEMENT_COLLISION_PADDING } from '../../constants.mjs';
|
|
11
|
-
import { useMentionSuggestions } from '../../shared.mjs';
|
|
12
12
|
import { withAutoFormatting } from '../../slate/plugins/auto-formatting.mjs';
|
|
13
13
|
import { withAutoLinks } from '../../slate/plugins/auto-links.mjs';
|
|
14
14
|
import { withCustomLinks } from '../../slate/plugins/custom-links.mjs';
|
|
15
15
|
import { withEmptyClearFormatting } from '../../slate/plugins/empty-clear-formatting.mjs';
|
|
16
16
|
import { getMentionDraftAtSelection, insertMention, insertMentionCharacter, MENTION_CHARACTER, withMentions } from '../../slate/plugins/mentions.mjs';
|
|
17
|
+
import { withNormalize } from '../../slate/plugins/normalize.mjs';
|
|
17
18
|
import { withPaste } from '../../slate/plugins/paste.mjs';
|
|
18
19
|
import { getDOMRange } from '../../slate/utils/get-dom-range.mjs';
|
|
19
20
|
import { isEmpty } from '../../slate/utils/is-empty.mjs';
|
|
@@ -50,15 +51,17 @@ function createComposerEditor({
|
|
|
50
51
|
createAttachments,
|
|
51
52
|
pasteFilesAsAttachments
|
|
52
53
|
}) {
|
|
53
|
-
return
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
54
|
+
return withNormalize(
|
|
55
|
+
withMentions(
|
|
56
|
+
withCustomLinks(
|
|
57
|
+
withAutoLinks(
|
|
58
|
+
withAutoFormatting(
|
|
59
|
+
withEmptyClearFormatting(
|
|
60
|
+
withPaste(withHistory(withReact(createEditor())), {
|
|
61
|
+
createAttachments,
|
|
62
|
+
pasteFilesAsAttachments
|
|
63
|
+
})
|
|
64
|
+
)
|
|
62
65
|
)
|
|
63
66
|
)
|
|
64
67
|
)
|
|
@@ -320,7 +323,15 @@ const ComposerSuggestionsList = forwardRef(({ children, asChild, ...props }, for
|
|
|
320
323
|
}, children);
|
|
321
324
|
});
|
|
322
325
|
const ComposerSuggestionsListItem = forwardRef(
|
|
323
|
-
({
|
|
326
|
+
({
|
|
327
|
+
value,
|
|
328
|
+
children,
|
|
329
|
+
onPointerMove,
|
|
330
|
+
onPointerDown,
|
|
331
|
+
onClick,
|
|
332
|
+
asChild,
|
|
333
|
+
...props
|
|
334
|
+
}, forwardedRef) => {
|
|
324
335
|
const ref = useRef(null);
|
|
325
336
|
const mergedRefs = useRefs(forwardedRef, ref);
|
|
326
337
|
const { selectedValue, setSelectedValue, itemId, onItemSelect } = useComposerSuggestionsContext(COMPOSER_SUGGESTIONS_LIST_ITEM_NAME);
|
|
@@ -347,18 +358,22 @@ const ComposerSuggestionsListItem = forwardRef(
|
|
|
347
358
|
const handlePointerDown = useCallback(
|
|
348
359
|
(event) => {
|
|
349
360
|
onPointerDown?.(event);
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
361
|
+
event.preventDefault();
|
|
362
|
+
event.stopPropagation();
|
|
363
|
+
},
|
|
364
|
+
[onPointerDown]
|
|
365
|
+
);
|
|
366
|
+
const handleClick = useCallback(
|
|
367
|
+
(event) => {
|
|
368
|
+
onClick?.(event);
|
|
369
|
+
const wasDefaultPrevented = event.isDefaultPrevented();
|
|
370
|
+
event.preventDefault();
|
|
371
|
+
event.stopPropagation();
|
|
372
|
+
if (!wasDefaultPrevented) {
|
|
373
|
+
onItemSelect(value);
|
|
359
374
|
}
|
|
360
375
|
},
|
|
361
|
-
[
|
|
376
|
+
[onClick, onItemSelect, value]
|
|
362
377
|
);
|
|
363
378
|
return /* @__PURE__ */ React__default.createElement(Component, {
|
|
364
379
|
role: "option",
|
|
@@ -367,6 +382,7 @@ const ComposerSuggestionsListItem = forwardRef(
|
|
|
367
382
|
"aria-selected": isSelected || void 0,
|
|
368
383
|
onPointerMove: handlePointerMove,
|
|
369
384
|
onPointerDown: handlePointerDown,
|
|
385
|
+
onClick: handleClick,
|
|
370
386
|
...props,
|
|
371
387
|
ref: mergedRefs
|
|
372
388
|
}, children);
|