@liveblocks/react-ui 2.15.2 → 2.16.0-toolbars1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/dist/_private/index.d.mts +56 -5
  2. package/dist/_private/index.d.ts +56 -5
  3. package/dist/_private/index.js +46 -0
  4. package/dist/_private/index.js.map +1 -1
  5. package/dist/_private/index.mjs +21 -0
  6. package/dist/_private/index.mjs.map +1 -1
  7. package/dist/components/Comment.js +13 -33
  8. package/dist/components/Comment.js.map +1 -1
  9. package/dist/components/Comment.mjs +15 -35
  10. package/dist/components/Comment.mjs.map +1 -1
  11. package/dist/components/Composer.js +25 -65
  12. package/dist/components/Composer.js.map +1 -1
  13. package/dist/components/Composer.mjs +26 -66
  14. package/dist/components/Composer.mjs.map +1 -1
  15. package/dist/components/InboxNotification.js +7 -17
  16. package/dist/components/InboxNotification.js.map +1 -1
  17. package/dist/components/InboxNotification.mjs +7 -17
  18. package/dist/components/InboxNotification.mjs.map +1 -1
  19. package/dist/components/Thread.js +1 -5
  20. package/dist/components/Thread.js.map +1 -1
  21. package/dist/components/Thread.mjs +1 -5
  22. package/dist/components/Thread.mjs.map +1 -1
  23. package/dist/components/internal/Button.js +23 -2
  24. package/dist/components/internal/Button.js.map +1 -1
  25. package/dist/components/internal/Button.mjs +24 -4
  26. package/dist/components/internal/Button.mjs.map +1 -1
  27. package/dist/components/internal/Dropdown.js +19 -8
  28. package/dist/components/internal/Dropdown.js.map +1 -1
  29. package/dist/components/internal/Dropdown.mjs +19 -8
  30. package/dist/components/internal/Dropdown.mjs.map +1 -1
  31. package/dist/components/internal/EmojiPicker.js +1 -3
  32. package/dist/components/internal/EmojiPicker.js.map +1 -1
  33. package/dist/components/internal/EmojiPicker.mjs +1 -3
  34. package/dist/components/internal/EmojiPicker.mjs.map +1 -1
  35. package/dist/components/internal/Tooltip.js +41 -15
  36. package/dist/components/internal/Tooltip.js.map +1 -1
  37. package/dist/components/internal/Tooltip.mjs +43 -16
  38. package/dist/components/internal/Tooltip.mjs.map +1 -1
  39. package/dist/icons/ChevronDown.js +16 -0
  40. package/dist/icons/ChevronDown.js.map +1 -0
  41. package/dist/icons/ChevronDown.mjs +14 -0
  42. package/dist/icons/ChevronDown.mjs.map +1 -0
  43. package/dist/icons/ChevronLeft.js +16 -0
  44. package/dist/icons/ChevronLeft.js.map +1 -0
  45. package/dist/icons/ChevronLeft.mjs +14 -0
  46. package/dist/icons/ChevronLeft.mjs.map +1 -0
  47. package/dist/icons/ChevronRight.js +16 -0
  48. package/dist/icons/ChevronRight.js.map +1 -0
  49. package/dist/icons/ChevronRight.mjs +14 -0
  50. package/dist/icons/ChevronRight.mjs.map +1 -0
  51. package/dist/icons/ChevronUp.js +16 -0
  52. package/dist/icons/ChevronUp.js.map +1 -0
  53. package/dist/icons/ChevronUp.mjs +14 -0
  54. package/dist/icons/ChevronUp.mjs.map +1 -0
  55. package/dist/icons/Comment.js +16 -0
  56. package/dist/icons/Comment.js.map +1 -0
  57. package/dist/icons/Comment.mjs +14 -0
  58. package/dist/icons/Comment.mjs.map +1 -0
  59. package/dist/icons/Lengthen.js +16 -0
  60. package/dist/icons/Lengthen.js.map +1 -0
  61. package/dist/icons/Lengthen.mjs +14 -0
  62. package/dist/icons/Lengthen.mjs.map +1 -0
  63. package/dist/icons/QuestionMark.js +26 -0
  64. package/dist/icons/QuestionMark.js.map +1 -0
  65. package/dist/icons/QuestionMark.mjs +24 -0
  66. package/dist/icons/QuestionMark.mjs.map +1 -0
  67. package/dist/icons/Redo.js +21 -0
  68. package/dist/icons/Redo.js.map +1 -0
  69. package/dist/icons/Redo.mjs +19 -0
  70. package/dist/icons/Redo.mjs.map +1 -0
  71. package/dist/icons/Shorten.js +16 -0
  72. package/dist/icons/Shorten.js.map +1 -0
  73. package/dist/icons/Shorten.mjs +14 -0
  74. package/dist/icons/Shorten.mjs.map +1 -0
  75. package/dist/icons/Sparkles.js +16 -0
  76. package/dist/icons/Sparkles.js.map +1 -0
  77. package/dist/icons/Sparkles.mjs +14 -0
  78. package/dist/icons/Sparkles.mjs.map +1 -0
  79. package/dist/icons/Translate.js +21 -0
  80. package/dist/icons/Translate.js.map +1 -0
  81. package/dist/icons/Translate.mjs +19 -0
  82. package/dist/icons/Translate.mjs.map +1 -0
  83. package/dist/icons/Underline.js +16 -0
  84. package/dist/icons/Underline.js.map +1 -0
  85. package/dist/icons/Underline.mjs +14 -0
  86. package/dist/icons/Underline.mjs.map +1 -0
  87. package/dist/icons/Undo.js +21 -0
  88. package/dist/icons/Undo.js.map +1 -0
  89. package/dist/icons/Undo.mjs +19 -0
  90. package/dist/icons/Undo.mjs.map +1 -0
  91. package/dist/icons/index.js +34 -0
  92. package/dist/icons/index.js.map +1 -1
  93. package/dist/icons/index.mjs +17 -0
  94. package/dist/icons/index.mjs.map +1 -1
  95. package/dist/index.d.mts +25 -25
  96. package/dist/index.d.ts +25 -25
  97. package/dist/primitives/Composer/index.js +0 -1
  98. package/dist/primitives/Composer/index.js.map +1 -1
  99. package/dist/primitives/Composer/index.mjs +0 -1
  100. package/dist/primitives/Composer/index.mjs.map +1 -1
  101. package/dist/primitives/index.d.mts +23 -23
  102. package/dist/primitives/index.d.ts +23 -23
  103. package/dist/version.js +1 -1
  104. package/dist/version.js.map +1 -1
  105. package/dist/version.mjs +1 -1
  106. package/dist/version.mjs.map +1 -1
  107. package/package.json +6 -6
  108. package/src/styles/index.css +142 -35
  109. package/styles.css +1 -1
  110. package/styles.css.map +1 -1
package/dist/index.d.mts CHANGED
@@ -3,23 +3,23 @@ import { ElementType, ComponentPropsWithoutRef, ReactNode, FormEvent, ComponentT
3
3
  import { CommentAttachment, CommentBody, BaseMetadata, DM, CommentData, HistoryVersion, InboxNotificationData, InboxNotificationThreadData, InboxNotificationTextMentionData, InboxNotificationCustomData, KDAD, ThreadData } from '@liveblocks/core';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
 
6
- declare type Direction = "ltr" | "rtl";
7
- declare type SlotProp = {
6
+ type Direction = "ltr" | "rtl";
7
+ type SlotProp = {
8
8
  /**
9
9
  * Replace the rendered element by the one passed as a child.
10
10
  */
11
11
  asChild?: boolean;
12
12
  };
13
- declare type ComponentPropsWithSlot<TElement extends ElementType<any>> = ComponentPropsWithoutRef<TElement> & SlotProp;
14
- declare type ComposerBodyText = {
13
+ type ComponentPropsWithSlot<TElement extends ElementType<any>> = ComponentPropsWithoutRef<TElement> & SlotProp;
14
+ type ComposerBodyText = {
15
15
  bold?: boolean;
16
16
  italic?: boolean;
17
17
  strikethrough?: boolean;
18
18
  code?: boolean;
19
19
  text: string;
20
20
  };
21
- declare type ComposerBodyMark = keyof Omit<ComposerBodyText, "text">;
22
- declare type ComposerBodyMarks = {
21
+ type ComposerBodyMark = keyof Omit<ComposerBodyText, "text">;
22
+ type ComposerBodyMarks = {
23
23
  [K in ComposerBodyMark]: boolean;
24
24
  };
25
25
  interface CommentAttachmentArgs {
@@ -94,7 +94,7 @@ interface HistoryVersionPreviewOverrides {
94
94
  HISTORY_VERSION_PREVIEW_EMPTY: ReactNode;
95
95
  HISTORY_VERSION_PREVIEW_ERROR: (error: Error) => ReactNode;
96
96
  }
97
- declare type Overrides = LocalizationOverrides & GlobalOverrides & ComposerOverrides & CommentOverrides & ThreadOverrides & InboxNotificationOverrides & HistoryVersionPreviewOverrides;
97
+ type Overrides = LocalizationOverrides & GlobalOverrides & ComposerOverrides & CommentOverrides & ThreadOverrides & InboxNotificationOverrides & HistoryVersionPreviewOverrides;
98
98
  declare function useOverrides(overrides?: Partial<Overrides>): Overrides;
99
99
 
100
100
  interface ComposerEditorMentionProps {
@@ -121,7 +121,7 @@ interface ComposerEditorLinkProps {
121
121
  */
122
122
  children: ReactNode;
123
123
  }
124
- declare type ComposerEditorMentionSuggestionsProps = {
124
+ type ComposerEditorMentionSuggestionsProps = {
125
125
  /**
126
126
  * The list of suggested user IDs.
127
127
  */
@@ -131,7 +131,7 @@ declare type ComposerEditorMentionSuggestionsProps = {
131
131
  */
132
132
  selectedUserId?: string;
133
133
  };
134
- declare type ComposerEditorFloatingToolbarProps = Record<string, never>;
134
+ type ComposerEditorFloatingToolbarProps = Record<string, never>;
135
135
  interface ComposerEditorComponents {
136
136
  /**
137
137
  * The component used to display mentions.
@@ -218,7 +218,7 @@ interface ComposerSubmitComment {
218
218
  attachments: CommentAttachment[];
219
219
  }
220
220
 
221
- declare type ComposerCreateThreadProps<M extends BaseMetadata> = {
221
+ type ComposerCreateThreadProps<M extends BaseMetadata> = {
222
222
  threadId?: never;
223
223
  commentId?: never;
224
224
  /**
@@ -226,7 +226,7 @@ declare type ComposerCreateThreadProps<M extends BaseMetadata> = {
226
226
  */
227
227
  metadata?: M;
228
228
  };
229
- declare type ComposerCreateCommentProps = {
229
+ type ComposerCreateCommentProps = {
230
230
  /**
231
231
  * The ID of the thread to reply to.
232
232
  */
@@ -234,7 +234,7 @@ declare type ComposerCreateCommentProps = {
234
234
  commentId?: never;
235
235
  metadata?: never;
236
236
  };
237
- declare type ComposerEditCommentProps = {
237
+ type ComposerEditCommentProps = {
238
238
  /**
239
239
  * The ID of the thread to edit a comment in.
240
240
  */
@@ -245,7 +245,7 @@ declare type ComposerEditCommentProps = {
245
245
  commentId: string;
246
246
  metadata?: never;
247
247
  };
248
- declare type ComposerProps<M extends BaseMetadata = DM> = Omit<ComponentPropsWithoutRef<"form">, "defaultValue"> & (ComposerCreateThreadProps<M> | ComposerCreateCommentProps | ComposerEditCommentProps) & {
248
+ type ComposerProps<M extends BaseMetadata = DM> = Omit<ComponentPropsWithoutRef<"form">, "defaultValue"> & (ComposerCreateThreadProps<M> | ComposerCreateCommentProps | ComposerEditCommentProps) & {
249
249
  /**
250
250
  * The event handler called when the composer is submitted.
251
251
  */
@@ -297,7 +297,7 @@ declare type ComposerProps<M extends BaseMetadata = DM> = Omit<ComponentPropsWit
297
297
  * @example
298
298
  * <Composer />
299
299
  */
300
- declare const Composer: <M extends BaseMetadata = BaseMetadata>(props: ComposerProps<M> & RefAttributes<HTMLFormElement>) => JSX.Element;
300
+ declare const Composer: <M extends BaseMetadata = DM>(props: ComposerProps<M> & RefAttributes<HTMLFormElement>) => JSX.Element;
301
301
 
302
302
  interface CommentProps extends ComponentPropsWithoutRef<"div"> {
303
303
  /**
@@ -377,7 +377,7 @@ interface HistoryVersionSummaryProps extends ComponentPropsWithoutRef<"button">
377
377
  */
378
378
  declare const HistoryVersionSummary: react.ForwardRefExoticComponent<HistoryVersionSummaryProps & react.RefAttributes<HTMLButtonElement>>;
379
379
 
380
- declare type HistoryVersionSummaryListProps = ComponentPropsWithoutRef<"ol">;
380
+ type HistoryVersionSummaryListProps = ComponentPropsWithoutRef<"ol">;
381
381
  /**
382
382
  * Displays versions summaries as a list.
383
383
  *
@@ -393,7 +393,7 @@ declare const HistoryVersionSummaryList: react.ForwardRefExoticComponent<Omit<re
393
393
  interface GlobalComponents {
394
394
  Anchor: ComponentType<ComponentPropsWithoutRef<"a">> | "a";
395
395
  }
396
- declare type Components = GlobalComponents;
396
+ type Components = GlobalComponents;
397
397
 
398
398
  interface AvatarProps extends ComponentProps<"div"> {
399
399
  /**
@@ -402,8 +402,8 @@ interface AvatarProps extends ComponentProps<"div"> {
402
402
  userId: string;
403
403
  }
404
404
 
405
- declare type ComponentTypeWithRef<T extends keyof JSX.IntrinsicElements, P> = ComponentType<P & Pick<ComponentProps<T>, "ref">>;
406
- declare type InboxNotificationKinds<KS extends KDAD = KDAD> = {
405
+ type ComponentTypeWithRef<T extends keyof JSX.IntrinsicElements, P> = ComponentType<P & Pick<ComponentProps<T>, "ref">>;
406
+ type InboxNotificationKinds<KS extends KDAD = KDAD> = {
407
407
  [K in KS]: ComponentTypeWithRef<"a", InboxNotificationCustomKindProps<K>>;
408
408
  } & {
409
409
  thread: ComponentTypeWithRef<"a", InboxNotificationThreadKindProps>;
@@ -484,17 +484,17 @@ interface InboxNotificationCustomProps extends Omit<InboxNotificationProps, "kin
484
484
  */
485
485
  markAsReadOnClick?: boolean;
486
486
  }
487
- declare type InboxNotificationThreadKindProps = Omit<InboxNotificationProps, "kinds"> & {
487
+ type InboxNotificationThreadKindProps = Omit<InboxNotificationProps, "kinds"> & {
488
488
  inboxNotification: InboxNotificationThreadData;
489
489
  };
490
- declare type InboxNotificationTextMentionKindProps = Omit<InboxNotificationProps, "kinds"> & {
490
+ type InboxNotificationTextMentionKindProps = Omit<InboxNotificationProps, "kinds"> & {
491
491
  inboxNotification: InboxNotificationTextMentionData;
492
492
  };
493
- declare type InboxNotificationCustomKindProps<K extends KDAD = KDAD> = Omit<InboxNotificationProps, "kinds"> & {
493
+ type InboxNotificationCustomKindProps<K extends KDAD = KDAD> = Omit<InboxNotificationProps, "kinds"> & {
494
494
  inboxNotification: InboxNotificationCustomData<K>;
495
495
  };
496
- declare type InboxNotificationIconProps = ComponentProps<"div">;
497
- declare type InboxNotificationAvatarProps = AvatarProps;
496
+ type InboxNotificationIconProps = ComponentProps<"div">;
497
+ type InboxNotificationAvatarProps = AvatarProps;
498
498
  declare function InboxNotificationIcon({ className, ...props }: InboxNotificationIconProps): react_jsx_runtime.JSX.Element;
499
499
  declare function InboxNotificationAvatar({ className, ...props }: InboxNotificationAvatarProps): react_jsx_runtime.JSX.Element;
500
500
  /**
@@ -626,9 +626,9 @@ interface ThreadProps<M extends BaseMetadata = DM> extends ComponentPropsWithout
626
626
  * ))}
627
627
  * </>
628
628
  */
629
- declare const Thread: <M extends BaseMetadata = BaseMetadata>(props: ThreadProps<M> & RefAttributes<HTMLDivElement>) => JSX.Element;
629
+ declare const Thread: <M extends BaseMetadata = DM>(props: ThreadProps<M> & RefAttributes<HTMLDivElement>) => JSX.Element;
630
630
 
631
- declare type LiveblocksUIConfigProps = PropsWithChildren<{
631
+ type LiveblocksUIConfigProps = PropsWithChildren<{
632
632
  /**
633
633
  * Override the components' strings.
634
634
  */
package/dist/index.d.ts CHANGED
@@ -3,23 +3,23 @@ import { ElementType, ComponentPropsWithoutRef, ReactNode, FormEvent, ComponentT
3
3
  import { CommentAttachment, CommentBody, BaseMetadata, DM, CommentData, HistoryVersion, InboxNotificationData, InboxNotificationThreadData, InboxNotificationTextMentionData, InboxNotificationCustomData, KDAD, ThreadData } from '@liveblocks/core';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
 
6
- declare type Direction = "ltr" | "rtl";
7
- declare type SlotProp = {
6
+ type Direction = "ltr" | "rtl";
7
+ type SlotProp = {
8
8
  /**
9
9
  * Replace the rendered element by the one passed as a child.
10
10
  */
11
11
  asChild?: boolean;
12
12
  };
13
- declare type ComponentPropsWithSlot<TElement extends ElementType<any>> = ComponentPropsWithoutRef<TElement> & SlotProp;
14
- declare type ComposerBodyText = {
13
+ type ComponentPropsWithSlot<TElement extends ElementType<any>> = ComponentPropsWithoutRef<TElement> & SlotProp;
14
+ type ComposerBodyText = {
15
15
  bold?: boolean;
16
16
  italic?: boolean;
17
17
  strikethrough?: boolean;
18
18
  code?: boolean;
19
19
  text: string;
20
20
  };
21
- declare type ComposerBodyMark = keyof Omit<ComposerBodyText, "text">;
22
- declare type ComposerBodyMarks = {
21
+ type ComposerBodyMark = keyof Omit<ComposerBodyText, "text">;
22
+ type ComposerBodyMarks = {
23
23
  [K in ComposerBodyMark]: boolean;
24
24
  };
25
25
  interface CommentAttachmentArgs {
@@ -94,7 +94,7 @@ interface HistoryVersionPreviewOverrides {
94
94
  HISTORY_VERSION_PREVIEW_EMPTY: ReactNode;
95
95
  HISTORY_VERSION_PREVIEW_ERROR: (error: Error) => ReactNode;
96
96
  }
97
- declare type Overrides = LocalizationOverrides & GlobalOverrides & ComposerOverrides & CommentOverrides & ThreadOverrides & InboxNotificationOverrides & HistoryVersionPreviewOverrides;
97
+ type Overrides = LocalizationOverrides & GlobalOverrides & ComposerOverrides & CommentOverrides & ThreadOverrides & InboxNotificationOverrides & HistoryVersionPreviewOverrides;
98
98
  declare function useOverrides(overrides?: Partial<Overrides>): Overrides;
99
99
 
100
100
  interface ComposerEditorMentionProps {
@@ -121,7 +121,7 @@ interface ComposerEditorLinkProps {
121
121
  */
122
122
  children: ReactNode;
123
123
  }
124
- declare type ComposerEditorMentionSuggestionsProps = {
124
+ type ComposerEditorMentionSuggestionsProps = {
125
125
  /**
126
126
  * The list of suggested user IDs.
127
127
  */
@@ -131,7 +131,7 @@ declare type ComposerEditorMentionSuggestionsProps = {
131
131
  */
132
132
  selectedUserId?: string;
133
133
  };
134
- declare type ComposerEditorFloatingToolbarProps = Record<string, never>;
134
+ type ComposerEditorFloatingToolbarProps = Record<string, never>;
135
135
  interface ComposerEditorComponents {
136
136
  /**
137
137
  * The component used to display mentions.
@@ -218,7 +218,7 @@ interface ComposerSubmitComment {
218
218
  attachments: CommentAttachment[];
219
219
  }
220
220
 
221
- declare type ComposerCreateThreadProps<M extends BaseMetadata> = {
221
+ type ComposerCreateThreadProps<M extends BaseMetadata> = {
222
222
  threadId?: never;
223
223
  commentId?: never;
224
224
  /**
@@ -226,7 +226,7 @@ declare type ComposerCreateThreadProps<M extends BaseMetadata> = {
226
226
  */
227
227
  metadata?: M;
228
228
  };
229
- declare type ComposerCreateCommentProps = {
229
+ type ComposerCreateCommentProps = {
230
230
  /**
231
231
  * The ID of the thread to reply to.
232
232
  */
@@ -234,7 +234,7 @@ declare type ComposerCreateCommentProps = {
234
234
  commentId?: never;
235
235
  metadata?: never;
236
236
  };
237
- declare type ComposerEditCommentProps = {
237
+ type ComposerEditCommentProps = {
238
238
  /**
239
239
  * The ID of the thread to edit a comment in.
240
240
  */
@@ -245,7 +245,7 @@ declare type ComposerEditCommentProps = {
245
245
  commentId: string;
246
246
  metadata?: never;
247
247
  };
248
- declare type ComposerProps<M extends BaseMetadata = DM> = Omit<ComponentPropsWithoutRef<"form">, "defaultValue"> & (ComposerCreateThreadProps<M> | ComposerCreateCommentProps | ComposerEditCommentProps) & {
248
+ type ComposerProps<M extends BaseMetadata = DM> = Omit<ComponentPropsWithoutRef<"form">, "defaultValue"> & (ComposerCreateThreadProps<M> | ComposerCreateCommentProps | ComposerEditCommentProps) & {
249
249
  /**
250
250
  * The event handler called when the composer is submitted.
251
251
  */
@@ -297,7 +297,7 @@ declare type ComposerProps<M extends BaseMetadata = DM> = Omit<ComponentPropsWit
297
297
  * @example
298
298
  * <Composer />
299
299
  */
300
- declare const Composer: <M extends BaseMetadata = BaseMetadata>(props: ComposerProps<M> & RefAttributes<HTMLFormElement>) => JSX.Element;
300
+ declare const Composer: <M extends BaseMetadata = DM>(props: ComposerProps<M> & RefAttributes<HTMLFormElement>) => JSX.Element;
301
301
 
302
302
  interface CommentProps extends ComponentPropsWithoutRef<"div"> {
303
303
  /**
@@ -377,7 +377,7 @@ interface HistoryVersionSummaryProps extends ComponentPropsWithoutRef<"button">
377
377
  */
378
378
  declare const HistoryVersionSummary: react.ForwardRefExoticComponent<HistoryVersionSummaryProps & react.RefAttributes<HTMLButtonElement>>;
379
379
 
380
- declare type HistoryVersionSummaryListProps = ComponentPropsWithoutRef<"ol">;
380
+ type HistoryVersionSummaryListProps = ComponentPropsWithoutRef<"ol">;
381
381
  /**
382
382
  * Displays versions summaries as a list.
383
383
  *
@@ -393,7 +393,7 @@ declare const HistoryVersionSummaryList: react.ForwardRefExoticComponent<Omit<re
393
393
  interface GlobalComponents {
394
394
  Anchor: ComponentType<ComponentPropsWithoutRef<"a">> | "a";
395
395
  }
396
- declare type Components = GlobalComponents;
396
+ type Components = GlobalComponents;
397
397
 
398
398
  interface AvatarProps extends ComponentProps<"div"> {
399
399
  /**
@@ -402,8 +402,8 @@ interface AvatarProps extends ComponentProps<"div"> {
402
402
  userId: string;
403
403
  }
404
404
 
405
- declare type ComponentTypeWithRef<T extends keyof JSX.IntrinsicElements, P> = ComponentType<P & Pick<ComponentProps<T>, "ref">>;
406
- declare type InboxNotificationKinds<KS extends KDAD = KDAD> = {
405
+ type ComponentTypeWithRef<T extends keyof JSX.IntrinsicElements, P> = ComponentType<P & Pick<ComponentProps<T>, "ref">>;
406
+ type InboxNotificationKinds<KS extends KDAD = KDAD> = {
407
407
  [K in KS]: ComponentTypeWithRef<"a", InboxNotificationCustomKindProps<K>>;
408
408
  } & {
409
409
  thread: ComponentTypeWithRef<"a", InboxNotificationThreadKindProps>;
@@ -484,17 +484,17 @@ interface InboxNotificationCustomProps extends Omit<InboxNotificationProps, "kin
484
484
  */
485
485
  markAsReadOnClick?: boolean;
486
486
  }
487
- declare type InboxNotificationThreadKindProps = Omit<InboxNotificationProps, "kinds"> & {
487
+ type InboxNotificationThreadKindProps = Omit<InboxNotificationProps, "kinds"> & {
488
488
  inboxNotification: InboxNotificationThreadData;
489
489
  };
490
- declare type InboxNotificationTextMentionKindProps = Omit<InboxNotificationProps, "kinds"> & {
490
+ type InboxNotificationTextMentionKindProps = Omit<InboxNotificationProps, "kinds"> & {
491
491
  inboxNotification: InboxNotificationTextMentionData;
492
492
  };
493
- declare type InboxNotificationCustomKindProps<K extends KDAD = KDAD> = Omit<InboxNotificationProps, "kinds"> & {
493
+ type InboxNotificationCustomKindProps<K extends KDAD = KDAD> = Omit<InboxNotificationProps, "kinds"> & {
494
494
  inboxNotification: InboxNotificationCustomData<K>;
495
495
  };
496
- declare type InboxNotificationIconProps = ComponentProps<"div">;
497
- declare type InboxNotificationAvatarProps = AvatarProps;
496
+ type InboxNotificationIconProps = ComponentProps<"div">;
497
+ type InboxNotificationAvatarProps = AvatarProps;
498
498
  declare function InboxNotificationIcon({ className, ...props }: InboxNotificationIconProps): react_jsx_runtime.JSX.Element;
499
499
  declare function InboxNotificationAvatar({ className, ...props }: InboxNotificationAvatarProps): react_jsx_runtime.JSX.Element;
500
500
  /**
@@ -626,9 +626,9 @@ interface ThreadProps<M extends BaseMetadata = DM> extends ComponentPropsWithout
626
626
  * ))}
627
627
  * </>
628
628
  */
629
- declare const Thread: <M extends BaseMetadata = BaseMetadata>(props: ThreadProps<M> & RefAttributes<HTMLDivElement>) => JSX.Element;
629
+ declare const Thread: <M extends BaseMetadata = DM>(props: ThreadProps<M> & RefAttributes<HTMLDivElement>) => JSX.Element;
630
630
 
631
- declare type LiveblocksUIConfigProps = PropsWithChildren<{
631
+ type LiveblocksUIConfigProps = PropsWithChildren<{
632
632
  /**
633
633
  * Override the components' strings.
634
634
  */
@@ -681,7 +681,6 @@ const ComposerEditor = react.forwardRef(
681
681
  }
682
682
  }
683
683
  if (isKey.isKey(event, "Escape")) {
684
- event.preventDefault();
685
684
  blur();
686
685
  }
687
686
  if (isKey.isKey(event, "Enter", { shift: false })) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../src/primitives/Composer/index.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type CommentAttachment,\n type CommentBody,\n type CommentLocalAttachment,\n createCommentAttachmentId,\n type EventSource,\n makeEventSource,\n} from \"@liveblocks/core\";\nimport {\n useClientOrNull,\n useLayoutEffect,\n useMentionSuggestions,\n useResolveMentionSuggestions,\n useRoomOrNull,\n useSyncSource,\n} from \"@liveblocks/react/_private\";\nimport { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport type {\n AriaAttributes,\n ChangeEvent,\n FocusEvent,\n FormEvent,\n KeyboardEvent,\n MouseEvent,\n PointerEvent,\n SyntheticEvent,\n} from \"react\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\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 Range as SlateRange,\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 { 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 {\n getMarks,\n leaveMarkEdge,\n toggleMark as toggleEditorMark,\n} from \"../../slate/utils/marks\";\nimport type {\n ComposerBody as ComposerBodyData,\n ComposerBodyAutoLink,\n ComposerBodyCustomLink,\n ComposerBodyMark,\n ComposerBodyMarks,\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 { useIndex } from \"../../utils/use-index\";\nimport { useInitial } from \"../../utils/use-initial\";\nimport { useObservable } from \"../../utils/use-observable\";\nimport { useRefs } from \"../../utils/use-refs\";\nimport { toAbsoluteUrl } from \"../Comment/utils\";\nimport {\n ComposerAttachmentsContext,\n ComposerContext,\n ComposerEditorContext,\n ComposerFloatingToolbarContext,\n ComposerSuggestionsContext,\n useComposer,\n useComposerAttachmentsContext,\n useComposerEditorContext,\n useComposerFloatingToolbarContext,\n useComposerSuggestionsContext,\n} from \"./contexts\";\nimport type {\n ComposerAttachFilesProps,\n ComposerAttachmentsDropAreaProps,\n ComposerEditorComponents,\n ComposerEditorElementProps,\n ComposerEditorFloatingToolbarWrapperProps,\n ComposerEditorLinkWrapperProps,\n ComposerEditorMentionSuggestionsWrapperProps,\n ComposerEditorMentionWrapperProps,\n ComposerEditorProps,\n ComposerFloatingToolbarProps,\n ComposerFormProps,\n ComposerLinkProps,\n ComposerMarkToggleProps,\n ComposerMentionProps,\n ComposerSubmitProps,\n ComposerSuggestionsListItemProps,\n ComposerSuggestionsListProps,\n ComposerSuggestionsProps,\n FloatingPosition,\n} from \"./types\";\nimport {\n commentBodyToComposerBody,\n composerBodyToCommentBody,\n getSideAndAlignFromFloatingPlacement,\n useComposerAttachmentsDropArea,\n useComposerAttachmentsManager,\n useContentZIndex,\n useFloatingWithOptions,\n} from \"./utils\";\n\nconst MENTION_SUGGESTIONS_POSITION: FloatingPosition = \"top\";\n\nconst FLOATING_TOOLBAR_POSITION: FloatingPosition = \"top\";\n\nconst COMPOSER_MENTION_NAME = \"ComposerMention\";\nconst COMPOSER_LINK_NAME = \"ComposerLink\";\nconst COMPOSER_FLOATING_TOOLBAR_NAME = \"ComposerFloatingToolbar\";\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_MARK_TOGGLE_NAME = \"ComposerMarkToggle\";\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 setMentionDraft,\n onItemSelect,\n position = MENTION_SUGGESTIONS_POSITION,\n dir,\n MentionSuggestions,\n}: ComposerEditorMentionSuggestionsWrapperProps) {\n const editor = useSlateStatic();\n const { onEditorChange } = useComposerEditorContext();\n const { isFocused } = useComposer();\n const { portalContainer } = useLiveblocksUIConfig();\n const [contentRef, contentZIndex] = useContentZIndex();\n const isOpen =\n isFocused && mentionDraft?.range !== undefined && userIds !== undefined;\n const {\n refs: { setReference, setFloating },\n strategy,\n isPositioned,\n placement,\n x,\n y,\n } = useFloatingWithOptions({\n position,\n dir,\n alignment: \"start\",\n open: isOpen,\n });\n\n useObservable(onEditorChange, () => {\n setMentionDraft(getMentionDraftAtSelection(editor));\n });\n\n useLayoutEffect(() => {\n if (!mentionDraft) {\n setReference(null);\n\n return;\n }\n\n const domRange = getDOMRange(editor, mentionDraft.range);\n setReference(domRange ?? null);\n }, [setReference, editor, mentionDraft]);\n\n return (\n <Persist>\n {isOpen ? (\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 ComposerEditorFloatingToolbarWrapper({\n id,\n position = FLOATING_TOOLBAR_POSITION,\n dir,\n FloatingToolbar,\n hasFloatingToolbarRange,\n setHasFloatingToolbarRange,\n}: ComposerEditorFloatingToolbarWrapperProps) {\n const editor = useSlateStatic();\n const { onEditorChange } = useComposerEditorContext();\n const { isFocused } = useComposer();\n const { portalContainer } = useLiveblocksUIConfig();\n const [contentRef, contentZIndex] = useContentZIndex();\n const [isPointerDown, setPointerDown] = useState(false);\n const isOpen = isFocused && !isPointerDown && hasFloatingToolbarRange;\n const {\n refs: { setReference, setFloating },\n strategy,\n isPositioned,\n placement,\n x,\n y,\n } = useFloatingWithOptions({\n type: \"range\",\n position,\n dir,\n alignment: \"center\",\n open: isOpen,\n });\n\n useLayoutEffect(() => {\n if (!isFocused) {\n return;\n }\n\n const handlePointerDown = () => setPointerDown(true);\n const handlePointerUp = () => setPointerDown(false);\n\n document.addEventListener(\"pointerdown\", handlePointerDown);\n document.addEventListener(\"pointerup\", handlePointerUp);\n\n return () => {\n document.removeEventListener(\"pointerdown\", handlePointerDown);\n document.removeEventListener(\"pointerup\", handlePointerUp);\n };\n }, [isFocused]);\n\n useObservable(onEditorChange, () => {\n // Detach from previous selection range (if any) to avoid sudden jumps\n setReference(null);\n\n // Then, wait for the next render to ensure the selection is updated\n requestAnimationFrame(() => {\n const domSelection = window.getSelection();\n\n // Finally, show the toolbar if there's a selection range\n if (\n !editor.selection ||\n SlateRange.isCollapsed(editor.selection) ||\n !domSelection ||\n !domSelection.rangeCount\n ) {\n setHasFloatingToolbarRange(false);\n setReference(null);\n } else {\n setHasFloatingToolbarRange(true);\n\n const domRange = domSelection.getRangeAt(0);\n setReference(domRange);\n }\n });\n });\n\n return (\n <Persist>\n {isOpen ? (\n <ComposerFloatingToolbarContext.Provider\n value={{\n id,\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 <FloatingToolbar />\n </Portal>\n </ComposerFloatingToolbarContext.Provider>\n ) : null}\n </Persist>\n );\n}\n\n/**\n * Displays a floating toolbar attached to the selection within `Composer.Editor`.\n *\n * @example\n * <Composer.FloatingToolbar>\n * <Composer.MarkToggle mark=\"bold\">Bold</Composer.MarkToggle>\n * <Composer.MarkToggle mark=\"italic\">Italic</Composer.MarkToggle>\n * </Composer.FloatingToolbar>\n */\nconst ComposerFloatingToolbar = forwardRef<\n HTMLDivElement,\n ComposerFloatingToolbarProps\n>(({ children, onPointerDown, style, asChild, ...props }, forwardedRef) => {\n const [isPresent] = usePersist();\n const ref = useRef<HTMLDivElement>(null);\n const {\n id,\n ref: contentRef,\n placement,\n dir,\n } = useComposerFloatingToolbarContext(COMPOSER_FLOATING_TOOLBAR_NAME);\n const mergedRefs = useRefs(forwardedRef, contentRef, ref);\n const [side, align] = useMemo(\n () => getSideAndAlignFromFloatingPlacement(placement),\n [placement]\n );\n const Component = asChild ? Slot : \"div\";\n useAnimationPersist(ref);\n\n const handlePointerDown = useCallback(\n (event: PointerEvent<HTMLDivElement>) => {\n onPointerDown?.(event);\n\n event.preventDefault();\n event.stopPropagation();\n },\n [onPointerDown]\n );\n\n return (\n <Component\n dir={dir}\n role=\"toolbar\"\n id={id}\n aria-label=\"Floating toolbar\"\n {...props}\n onPointerDown={handlePointerDown}\n data-state={isPresent ? \"open\" : \"closed\"}\n data-side={side}\n data-align={align}\n style={{\n display: \"flex\",\n flexDirection: \"row\",\n maxWidth: \"var(--lb-composer-floating-available-width)\",\n overflowX: \"auto\",\n ...style,\n }}\n ref={mergedRefs}\n >\n {children}\n </Component>\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 () => getSideAndAlignFromFloatingPlacement(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-floating-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 client = useClientOrNull();\n const { editor, validate, setFocused, onEditorChange, roomId } =\n useComposerEditorContext();\n const {\n submit,\n focus,\n blur,\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, FloatingToolbar } = useMemo(\n () => ({ ...defaultEditorComponents, ...components }),\n [components]\n );\n\n const [hasFloatingToolbarRange, setHasFloatingToolbarRange] =\n useState(false);\n // If used with LiveblocksProvider but without resolveMentionSuggestions,\n // we can skip the mention suggestions logic entirely\n const resolveMentionSuggestions = useResolveMentionSuggestions();\n const hasResolveMentionSuggestions = client\n ? resolveMentionSuggestions\n : true;\n const [mentionDraft, setMentionDraft] = useState<MentionDraft>();\n const mentionSuggestions = useMentionSuggestions(\n roomId,\n mentionDraft?.text\n );\n const [\n selectedMentionSuggestionIndex,\n setPreviousSelectedMentionSuggestionIndex,\n setNextSelectedMentionSuggestionIndex,\n setSelectedMentionSuggestionIndex,\n ] = useIndex(0, mentionSuggestions?.length ?? 0);\n const id = useId();\n const floatingToolbarId = useMemo(\n () => `liveblocks-floating-toolbar-${id}`,\n [id]\n );\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\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 // Our multi-component setup requires us to instantiate the editor in `Composer.Form`\n // but we can only listen to changes here in `Composer.Editor` via `Slate`, so we use\n // an event source to notify `Composer.Form` of changes.\n onEditorChange.notify();\n },\n [validate, onEditorChange]\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 if (hasFloatingToolbarRange) {\n // Close the floating toolbar on Escape\n if (isKey(event, \"Escape\")) {\n event.preventDefault();\n setHasFloatingToolbarRange(false);\n }\n }\n\n // Blur the editor on Escape\n if (isKey(event, \"Escape\")) {\n event.preventDefault();\n blur();\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 toggleEditorMark(editor, \"bold\");\n }\n\n // Toggle italic on Command/Control + I\n if (isKey(event, \"i\", { mod: true })) {\n event.preventDefault();\n toggleEditorMark(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 toggleEditorMark(editor, \"strikethrough\");\n }\n\n // Toggle code on Command/Control + E\n if (isKey(event, \"e\", { mod: true })) {\n event.preventDefault();\n toggleEditorMark(editor, \"code\");\n }\n }\n },\n [\n onKeyDown,\n mentionDraft,\n mentionSuggestions,\n hasFloatingToolbarRange,\n editor,\n setNextSelectedMentionSuggestionIndex,\n setPreviousSelectedMentionSuggestionIndex,\n selectedMentionSuggestionIndex,\n createMention,\n setSelectedMentionSuggestionIndex,\n blur,\n canSubmit,\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 additionalProps: 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 : hasFloatingToolbarRange\n ? {\n \"aria-haspopup\": true,\n \"aria-controls\": floatingToolbarId,\n }\n : {},\n [\n mentionDraft,\n suggestionsListId,\n suggestionsListItemId,\n selectedMentionSuggestionUserId,\n hasFloatingToolbarRange,\n floatingToolbarId,\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 useLayoutEffect(() => {\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 useLayoutEffect(() => {\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 {...additionalProps}\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 {hasResolveMentionSuggestions && (\n <ComposerEditorMentionSuggestionsWrapper\n dir={dir}\n mentionDraft={mentionDraft}\n setMentionDraft={setMentionDraft}\n selectedUserId={selectedMentionSuggestionUserId}\n setSelectedUserId={setSelectedMentionSuggestionUserId}\n userIds={mentionSuggestions}\n id={suggestionsListId}\n itemId={suggestionsListItemId}\n onItemSelect={createMention}\n MentionSuggestions={MentionSuggestions}\n />\n )}\n {FloatingToolbar && (\n <ComposerEditorFloatingToolbarWrapper\n dir={dir}\n id={floatingToolbarId}\n hasFloatingToolbarRange={hasFloatingToolbarRange}\n setHasFloatingToolbarRange={setHasFloatingToolbarRange}\n FloatingToolbar={FloatingToolbar}\n />\n )}\n </Slate>\n );\n }\n);\n\nconst MAX_ATTACHMENTS = 10;\nconst MAX_ATTACHMENT_SIZE = 1024 * 1024 * 1024; // 1 GB\n\nfunction prepareAttachment(file: File): CommentLocalAttachment {\n return {\n type: \"localAttachment\",\n status: \"idle\",\n id: createCommentAttachmentId(),\n name: file.name,\n size: file.size,\n mimeType: file.type,\n file,\n };\n}\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 preventUnsavedChanges = true,\n disabled,\n asChild,\n roomId: _roomId,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"form\";\n const [isEmpty, setEmpty] = useState(true);\n const [isSubmitting, setSubmitting] = useState(false);\n const [isFocused, setFocused] = useState(false);\n const room = useRoomOrNull();\n\n const roomId = _roomId !== undefined ? _roomId : room?.id;\n if (roomId === undefined) {\n throw new Error(\"Composer.Form must be a descendant of RoomProvider.\");\n }\n\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\n const {\n attachments,\n isUploadingAttachments,\n addAttachments,\n removeAttachment,\n clearAttachments,\n } = useComposerAttachmentsManager(defaultAttachments, {\n maxFileSize: maxAttachmentSize,\n roomId,\n });\n const numberOfAttachments = attachments.length;\n const hasMaxAttachments = numberOfAttachments >= maxAttachments;\n\n const isDisabled = useMemo(() => {\n return isSubmitting || disabled === true;\n }, [isSubmitting, disabled]);\n const canSubmit = useMemo(() => {\n return !isEmpty && !isUploadingAttachments;\n }, [isEmpty, isUploadingAttachments]);\n const [marks, setMarks] = useState<ComposerBodyMarks>(getMarks);\n\n const ref = useRef<HTMLFormElement>(null);\n const mergedRefs = useRefs(forwardedRef, ref);\n const fileInputRef = useRef<HTMLInputElement>(null);\n const syncSource = useSyncSource();\n\n // Mark the composer as a pending update when it has unsubmitted (draft)\n // text or attachments\n const isPending = !preventUnsavedChanges\n ? false\n : !isEmpty || isUploadingAttachments || attachments.length > 0;\n\n useEffect(() => {\n syncSource?.setSyncStatus(\n isPending ? \"has-local-changes\" : \"synchronized\"\n );\n }, [syncSource, isPending]);\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) => prepareAttachment(file));\n\n addAttachments(attachments);\n },\n [addAttachments, maxAttachments, numberOfAttachments]\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 const onEditorChange = useInitial(makeEventSource) as EventSource<void>;\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, SlateEditor.end(editor, []));\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 const toggleMark = useCallback(\n (mark: ComposerBodyMark) => {\n toggleEditorMark(editor, mark);\n },\n [editor]\n );\n\n useObservable(onEditorChange, () => {\n setMarks(getMarks(editor));\n });\n\n return (\n <ComposerEditorContext.Provider\n value={{\n editor,\n validate,\n setFocused,\n onEditorChange,\n roomId,\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 toggleMark,\n marks,\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\n/**\n * A toggle button which toggles a specific text mark.\n *\n * @example\n * <Composer.MarkToggle mark=\"bold\">\n * Bold\n * </Composer.MarkToggle>\n */\nconst ComposerMarkToggle = forwardRef<\n HTMLButtonElement,\n ComposerMarkToggleProps\n>(\n (\n { children, mark, onValueChange, onClick, asChild, ...props },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"button\";\n const { marks, toggleMark } = useComposer();\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n toggleMark(mark);\n onValueChange?.(mark);\n }\n },\n [mark, onClick, onValueChange, toggleMark]\n );\n\n return (\n <TogglePrimitive.Root\n asChild\n pressed={marks[mark]}\n onClick={handleClick}\n {...props}\n >\n <Component {...props} ref={forwardedRef}>\n {children}\n </Component>\n </TogglePrimitive.Root>\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 ComposerFloatingToolbar.displayName = COMPOSER_FLOATING_TOOLBAR_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 ComposerMarkToggle.displayName = COMPOSER_MARK_TOGGLE_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 ComposerFloatingToolbar as FloatingToolbar,\n ComposerForm as Form,\n ComposerLink as Link,\n ComposerMarkToggle as MarkToggle,\n ComposerMention as Mention,\n ComposerSubmit as Submit,\n ComposerSuggestions as Suggestions,\n ComposerSuggestionsList as SuggestionsList,\n ComposerSuggestionsListItem as SuggestionsListItem,\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAqJA;AAEA;AAEA;AACA;AACA;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;AACP;AACE;AAAwB;AAAI;AAC3B;AACH;AAAA;AAGP;AAEA;AAAmC;AACjC;AACA;AACA;AAEF;AACE;AAAa;AACiC;AAChC;AAGd;AACG;AAAS;AACP;AAAK;AAAa;AAAS;AAGlC;AAEA;AAAiD;AAC/C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACW;AACX;AAEF;AACE;AACA;AACA;AACA;AACA;AACA;AAEA;AAAM;AAC8B;AAClC;AACA;AACA;AACA;AACA;AACyB;AACzB;AACA;AACW;AACL;AAGR;AACE;AAAkD;AAGpD;AACE;AACE;AAEA;AAAA;AAGF;AACA;AAA6B;AAG/B;AACG;AAEI;AACQ;AACL;AACA;AACe;AACG;AAClB;AACA;AACA;AACK;AACP;AAEC;AACM;AACM;AACJ;AACK;AACL;AACC;AAGF;AACM;AACF;AACV;AAEC;AACC;AACA;AACF;AACF;AAEA;AAGV;AAEA;AAA8C;AAC5C;AACW;AACX;AACA;AACA;AAEF;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAM;AAC8B;AAClC;AACA;AACA;AACA;AACA;AACyB;AACnB;AACN;AACA;AACW;AACL;AAGR;AACE;AACE;AAAA;AAGF;AACA;AAEA;AACA;AAEA;AACE;AACA;AAAyD;AAC3D;AAGF;AAEE;AAGA;AACE;AAGA;AAME;AACA;AAAiB;AAEjB;AAEA;AACA;AAAqB;AACvB;AACD;AAGH;AACG;AAEI;AACQ;AACL;AACA;AACA;AACK;AACP;AAEC;AACM;AACM;AACJ;AACK;AACL;AACC;AAGF;AACM;AACF;AACV;AAEiB;AACnB;AAEA;AAGV;AAWM;AAIJ;AACA;AACA;AAAM;AACJ;AACK;AACL;AACA;AAEF;AACA;AAAsB;AACgC;AAC1C;AAEZ;AACA;AAEA;AAA0B;AAEtB;AAEA;AACA;AAAsB;AACxB;AACc;AAGhB;AACG;AACC;AACK;AACL;AACW;AACP;AACW;AACkB;AACtB;AACC;AACL;AACI;AACM;AACL;AACC;AACR;AACL;AACK;AAEJ;AAGP;AAEA;AAA+B;AAC7B;AACA;AAEF;AAEE;AAEA;AAAsB;AAElB;AACG;AACC;AACK;AACP;AAEC;AAEH;AACG;AACC;AACK;AAGP;AAGF;AACG;AAAM;AAA0C;AAC9C;AACH;AAGF;AAAO;AAEb;AAGA;AACE;AACE;AAAY;AAAQ;AAAS;AAG/B;AACE;AAAY;AAAI;AAAS;AAG3B;AACE;AAAY;AAAG;AAAS;AAG1B;AACE;AAAY;AAAM;AAAS;AAG7B;AAAQ;AAAS;AAAa;AAChC;AAEA;AAAmC;AACjC;AAEF;AACE;AAEA;AACG;AAAS;AAAY;AAA+B;AAClD;AAGP;AAQA;AAAwB;AAEpB;AACA;AAEA;AACG;AAC8B;AACzB;AACC;AAEJ;AACH;AAGN;AAQA;AAAqB;AAEjB;AAEA;AACG;AACQ;AACH;AACA;AACC;AAEJ;AACH;AAGN;AAKM;AAIJ;AACA;AACA;AAAM;AACC;AACL;AACA;AAEF;AACA;AAAsB;AACgC;AAC1C;AAEZ;AACA;AAEA;AACG;AACC;AACI;AAC6B;AACtB;AACC;AACL;AACI;AACM;AACJ;AACA;AACR;AACL;AACK;AAEJ;AAGP;AAcM;AAIJ;AACA;AAEA;AACG;AACM;AACL;AACW;AACP;AACC;AAEJ;AAGP;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;AAEJ;AACH;AAGN;AAEA;AAA0D;AAEtD;AAAQ;AAAa;AAAa;AAAS;AAC7C;AAEE;AACG;AACE;AAAA;AACA;AAAA;AACH;AAEJ;AAEE;AACG;AACE;AAEI;AAAgD;AAC9C;AAEJ;AACH;AAEA;AAER;AAQA;AAAuB;AAEnB;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AAEA;AAAM;AACJ;AACA;AACA;AACA;AACA;AACY;AACZ;AAEF;AACA;AACA;AACE;AAA4C;AAE9C;AAA+D;AACV;AACxC;AAGb;AAIA;AACA;AAGA;AACA;AAA2B;AACzB;AACc;AAEhB;AAAM;AACJ;AACA;AACA;AACA;AAEF;AACA;AAA0B;AACa;AAClC;AAEL;AAA0B;AACa;AAClC;AAEL;AAA8B;AAEoC;AAC7D;AAGL;AAAsB;AAElB;AACG;AAAsB;AAAkB;AAAgB;AAAO;AAEpE;AACc;AAGhB;AAAqB;AAEjB;AAKA;AAAsB;AACxB;AACyB;AAG3B;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;AAEA;AAEE;AACE;AACA;AAAgC;AAClC;AAIF;AACE;AACA;AAAK;AAIP;AAEE;AAEA;AACE;AAAO;AACT;AAIF;AACE;AACA;AAAmB;AAIrB;AACE;AACA;AAA+B;AAIjC;AACE;AACA;AAAiC;AAInC;AACE;AACA;AAAwC;AAI1C;AACE;AACA;AAA+B;AACjC;AACF;AACF;AACA;AACE;AACA;AACA;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;AAAwC;AAGhC;AACQ;AACe;AACJ;AACA;AACQ;AACvB;AACF;AAGA;AACmB;AACA;AAElB;AACT;AACE;AACA;AACA;AACA;AACA;AACA;AACF;AAGF;AACE;AAA2C;AAI7C;AACE;AACE;AAAM;AACR;AAKF;AACE;AACE;AAAO;AACT;AAGF;AACG;AACC;AACc;AACJ;AAEV;AAAC;AACC;AACuC;AACxB;AACJ;AACgB;AACE;AACzB;AACA;AACM;AACA;AACC;AACF;AACD;AACR;AACY;AACO;AACrB;AAEG;AACC;AACA;AACA;AACgB;AACG;AACV;AACL;AACI;AACM;AACd;AACF;AAGC;AACC;AACI;AACJ;AACA;AACA;AACF;AAAA;AAEJ;AAGN;AAEA;AACA;AAEA;AACE;AAAO;AACC;AACE;AACsB;AACnB;AACA;AACI;AACf;AAEJ;AAWA;AAAqB;AAEjB;AACE;AACA;AACA;AACsB;AACtB;AACwB;AACxB;AACA;AACQ;AACL;AAIL;AACA;AACA;AACA;AACA;AAEA;AACA;AACE;AAAqE;AAIvE;AACA;AAEA;AAAM;AACJ;AACA;AACA;AACA;AACA;AACoD;AACvC;AACb;AAEF;AACA;AAEA;AACE;AAAoC;AAEtC;AACE;AAAoB;AAEtB;AAEA;AACA;AACA;AACA;AAIA;AAIA;AACE;AAAY;AACwB;AACpC;AAGF;AAA0B;AAEtB;AACE;AAAA;AAGF;AAAmC;AACjC;AACiB;AAGnB;AAEA;AAEA;AAA0B;AAC5B;AACoD;AAGtD;AAEA;AACE;AAA+B;AAGjC;AACE;AAAkC;AAGpC;AAAe;AACQ;AACA;AACnB;AACD;AAEH;AAEA;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;AAA0D;AAG5D;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;AAAmB;AAEf;AAA6B;AAC/B;AACO;AAGT;AACE;AAAyB;AAG3B;AACG;AACQ;AACL;AACA;AACA;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;AACA;AACA;AACF;AAEC;AAAc;AAAiB;AAAmB;AACjD;AAAC;AACM;AACG;AACH;AACK;AACD;AACC;AACe;AAC3B;AACC;AAAW;AAAS;AAAA;AACvB;AACF;AACF;AACF;AAGN;AAQA;AAAuB;AAEnB;AACA;AACA;AAEA;AACG;AACM;AACD;AACC;AACK;AAET;AACH;AAGN;AAQM;AAIJ;AACA;AACA;AACA;AAEA;AAAoB;AAEhB;AAEA;AACE;AAAY;AACd;AACF;AACqB;AAGvB;AACG;AACM;AACD;AACK;AACJ;AACK;AAET;AAGP;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;AAUA;AAA2B;AAQvB;AACA;AAEA;AAAoB;AAEhB;AAEA;AACE;AACA;AAAoB;AACtB;AACF;AACyC;AAG3C;AACG;AACQ;AACQ;AACN;AACL;AAEH;AAAc;AAAY;AACxB;AACH;AACF;AAGN;AAEA;AACE;AACA;AACA;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 {\n type CommentAttachment,\n type CommentBody,\n type CommentLocalAttachment,\n createCommentAttachmentId,\n type EventSource,\n makeEventSource,\n} from \"@liveblocks/core\";\nimport {\n useClientOrNull,\n useLayoutEffect,\n useMentionSuggestions,\n useResolveMentionSuggestions,\n useRoomOrNull,\n useSyncSource,\n} from \"@liveblocks/react/_private\";\nimport { Slot, Slottable } from \"@radix-ui/react-slot\";\nimport * as TogglePrimitive from \"@radix-ui/react-toggle\";\nimport type {\n AriaAttributes,\n ChangeEvent,\n FocusEvent,\n FormEvent,\n KeyboardEvent,\n MouseEvent,\n PointerEvent,\n SyntheticEvent,\n} from \"react\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useId,\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 Range as SlateRange,\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 { 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 {\n getMarks,\n leaveMarkEdge,\n toggleMark as toggleEditorMark,\n} from \"../../slate/utils/marks\";\nimport type {\n ComposerBody as ComposerBodyData,\n ComposerBodyAutoLink,\n ComposerBodyCustomLink,\n ComposerBodyMark,\n ComposerBodyMarks,\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 { useIndex } from \"../../utils/use-index\";\nimport { useInitial } from \"../../utils/use-initial\";\nimport { useObservable } from \"../../utils/use-observable\";\nimport { useRefs } from \"../../utils/use-refs\";\nimport { toAbsoluteUrl } from \"../Comment/utils\";\nimport {\n ComposerAttachmentsContext,\n ComposerContext,\n ComposerEditorContext,\n ComposerFloatingToolbarContext,\n ComposerSuggestionsContext,\n useComposer,\n useComposerAttachmentsContext,\n useComposerEditorContext,\n useComposerFloatingToolbarContext,\n useComposerSuggestionsContext,\n} from \"./contexts\";\nimport type {\n ComposerAttachFilesProps,\n ComposerAttachmentsDropAreaProps,\n ComposerEditorComponents,\n ComposerEditorElementProps,\n ComposerEditorFloatingToolbarWrapperProps,\n ComposerEditorLinkWrapperProps,\n ComposerEditorMentionSuggestionsWrapperProps,\n ComposerEditorMentionWrapperProps,\n ComposerEditorProps,\n ComposerFloatingToolbarProps,\n ComposerFormProps,\n ComposerLinkProps,\n ComposerMarkToggleProps,\n ComposerMentionProps,\n ComposerSubmitProps,\n ComposerSuggestionsListItemProps,\n ComposerSuggestionsListProps,\n ComposerSuggestionsProps,\n FloatingPosition,\n} from \"./types\";\nimport {\n commentBodyToComposerBody,\n composerBodyToCommentBody,\n getSideAndAlignFromFloatingPlacement,\n useComposerAttachmentsDropArea,\n useComposerAttachmentsManager,\n useContentZIndex,\n useFloatingWithOptions,\n} from \"./utils\";\n\nconst MENTION_SUGGESTIONS_POSITION: FloatingPosition = \"top\";\n\nconst FLOATING_TOOLBAR_POSITION: FloatingPosition = \"top\";\n\nconst COMPOSER_MENTION_NAME = \"ComposerMention\";\nconst COMPOSER_LINK_NAME = \"ComposerLink\";\nconst COMPOSER_FLOATING_TOOLBAR_NAME = \"ComposerFloatingToolbar\";\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_MARK_TOGGLE_NAME = \"ComposerMarkToggle\";\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 setMentionDraft,\n onItemSelect,\n position = MENTION_SUGGESTIONS_POSITION,\n dir,\n MentionSuggestions,\n}: ComposerEditorMentionSuggestionsWrapperProps) {\n const editor = useSlateStatic();\n const { onEditorChange } = useComposerEditorContext();\n const { isFocused } = useComposer();\n const { portalContainer } = useLiveblocksUIConfig();\n const [contentRef, contentZIndex] = useContentZIndex();\n const isOpen =\n isFocused && mentionDraft?.range !== undefined && userIds !== undefined;\n const {\n refs: { setReference, setFloating },\n strategy,\n isPositioned,\n placement,\n x,\n y,\n } = useFloatingWithOptions({\n position,\n dir,\n alignment: \"start\",\n open: isOpen,\n });\n\n useObservable(onEditorChange, () => {\n setMentionDraft(getMentionDraftAtSelection(editor));\n });\n\n useLayoutEffect(() => {\n if (!mentionDraft) {\n setReference(null);\n\n return;\n }\n\n const domRange = getDOMRange(editor, mentionDraft.range);\n setReference(domRange ?? null);\n }, [setReference, editor, mentionDraft]);\n\n return (\n <Persist>\n {isOpen ? (\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 ComposerEditorFloatingToolbarWrapper({\n id,\n position = FLOATING_TOOLBAR_POSITION,\n dir,\n FloatingToolbar,\n hasFloatingToolbarRange,\n setHasFloatingToolbarRange,\n}: ComposerEditorFloatingToolbarWrapperProps) {\n const editor = useSlateStatic();\n const { onEditorChange } = useComposerEditorContext();\n const { isFocused } = useComposer();\n const { portalContainer } = useLiveblocksUIConfig();\n const [contentRef, contentZIndex] = useContentZIndex();\n const [isPointerDown, setPointerDown] = useState(false);\n const isOpen = isFocused && !isPointerDown && hasFloatingToolbarRange;\n const {\n refs: { setReference, setFloating },\n strategy,\n isPositioned,\n placement,\n x,\n y,\n } = useFloatingWithOptions({\n type: \"range\",\n position,\n dir,\n alignment: \"center\",\n open: isOpen,\n });\n\n useLayoutEffect(() => {\n if (!isFocused) {\n return;\n }\n\n const handlePointerDown = () => setPointerDown(true);\n const handlePointerUp = () => setPointerDown(false);\n\n document.addEventListener(\"pointerdown\", handlePointerDown);\n document.addEventListener(\"pointerup\", handlePointerUp);\n\n return () => {\n document.removeEventListener(\"pointerdown\", handlePointerDown);\n document.removeEventListener(\"pointerup\", handlePointerUp);\n };\n }, [isFocused]);\n\n useObservable(onEditorChange, () => {\n // Detach from previous selection range (if any) to avoid sudden jumps\n setReference(null);\n\n // Then, wait for the next render to ensure the selection is updated\n requestAnimationFrame(() => {\n const domSelection = window.getSelection();\n\n // Finally, show the toolbar if there's a selection range\n if (\n !editor.selection ||\n SlateRange.isCollapsed(editor.selection) ||\n !domSelection ||\n !domSelection.rangeCount\n ) {\n setHasFloatingToolbarRange(false);\n setReference(null);\n } else {\n setHasFloatingToolbarRange(true);\n\n const domRange = domSelection.getRangeAt(0);\n setReference(domRange);\n }\n });\n });\n\n return (\n <Persist>\n {isOpen ? (\n <ComposerFloatingToolbarContext.Provider\n value={{\n id,\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 <FloatingToolbar />\n </Portal>\n </ComposerFloatingToolbarContext.Provider>\n ) : null}\n </Persist>\n );\n}\n\n/**\n * Displays a floating toolbar attached to the selection within `Composer.Editor`.\n *\n * @example\n * <Composer.FloatingToolbar>\n * <Composer.MarkToggle mark=\"bold\">Bold</Composer.MarkToggle>\n * <Composer.MarkToggle mark=\"italic\">Italic</Composer.MarkToggle>\n * </Composer.FloatingToolbar>\n */\nconst ComposerFloatingToolbar = forwardRef<\n HTMLDivElement,\n ComposerFloatingToolbarProps\n>(({ children, onPointerDown, style, asChild, ...props }, forwardedRef) => {\n const [isPresent] = usePersist();\n const ref = useRef<HTMLDivElement>(null);\n const {\n id,\n ref: contentRef,\n placement,\n dir,\n } = useComposerFloatingToolbarContext(COMPOSER_FLOATING_TOOLBAR_NAME);\n const mergedRefs = useRefs(forwardedRef, contentRef, ref);\n const [side, align] = useMemo(\n () => getSideAndAlignFromFloatingPlacement(placement),\n [placement]\n );\n const Component = asChild ? Slot : \"div\";\n useAnimationPersist(ref);\n\n const handlePointerDown = useCallback(\n (event: PointerEvent<HTMLDivElement>) => {\n onPointerDown?.(event);\n\n event.preventDefault();\n event.stopPropagation();\n },\n [onPointerDown]\n );\n\n return (\n <Component\n dir={dir}\n role=\"toolbar\"\n id={id}\n aria-label=\"Floating toolbar\"\n {...props}\n onPointerDown={handlePointerDown}\n data-state={isPresent ? \"open\" : \"closed\"}\n data-side={side}\n data-align={align}\n style={{\n display: \"flex\",\n flexDirection: \"row\",\n maxWidth: \"var(--lb-composer-floating-available-width)\",\n overflowX: \"auto\",\n ...style,\n }}\n ref={mergedRefs}\n >\n {children}\n </Component>\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 () => getSideAndAlignFromFloatingPlacement(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-floating-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 client = useClientOrNull();\n const { editor, validate, setFocused, onEditorChange, roomId } =\n useComposerEditorContext();\n const {\n submit,\n focus,\n blur,\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, FloatingToolbar } = useMemo(\n () => ({ ...defaultEditorComponents, ...components }),\n [components]\n );\n\n const [hasFloatingToolbarRange, setHasFloatingToolbarRange] =\n useState(false);\n // If used with LiveblocksProvider but without resolveMentionSuggestions,\n // we can skip the mention suggestions logic entirely\n const resolveMentionSuggestions = useResolveMentionSuggestions();\n const hasResolveMentionSuggestions = client\n ? resolveMentionSuggestions\n : true;\n const [mentionDraft, setMentionDraft] = useState<MentionDraft>();\n const mentionSuggestions = useMentionSuggestions(\n roomId,\n mentionDraft?.text\n );\n const [\n selectedMentionSuggestionIndex,\n setPreviousSelectedMentionSuggestionIndex,\n setNextSelectedMentionSuggestionIndex,\n setSelectedMentionSuggestionIndex,\n ] = useIndex(0, mentionSuggestions?.length ?? 0);\n const id = useId();\n const floatingToolbarId = useMemo(\n () => `liveblocks-floating-toolbar-${id}`,\n [id]\n );\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\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 // Our multi-component setup requires us to instantiate the editor in `Composer.Form`\n // but we can only listen to changes here in `Composer.Editor` via `Slate`, so we use\n // an event source to notify `Composer.Form` of changes.\n onEditorChange.notify();\n },\n [validate, onEditorChange]\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 if (hasFloatingToolbarRange) {\n // Close the floating toolbar on Escape\n if (isKey(event, \"Escape\")) {\n event.preventDefault();\n setHasFloatingToolbarRange(false);\n }\n }\n\n // Blur the editor on Escape\n if (isKey(event, \"Escape\")) {\n blur();\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 toggleEditorMark(editor, \"bold\");\n }\n\n // Toggle italic on Command/Control + I\n if (isKey(event, \"i\", { mod: true })) {\n event.preventDefault();\n toggleEditorMark(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 toggleEditorMark(editor, \"strikethrough\");\n }\n\n // Toggle code on Command/Control + E\n if (isKey(event, \"e\", { mod: true })) {\n event.preventDefault();\n toggleEditorMark(editor, \"code\");\n }\n }\n },\n [\n onKeyDown,\n mentionDraft,\n mentionSuggestions,\n hasFloatingToolbarRange,\n editor,\n setNextSelectedMentionSuggestionIndex,\n setPreviousSelectedMentionSuggestionIndex,\n selectedMentionSuggestionIndex,\n createMention,\n setSelectedMentionSuggestionIndex,\n blur,\n canSubmit,\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 additionalProps: 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 : hasFloatingToolbarRange\n ? {\n \"aria-haspopup\": true,\n \"aria-controls\": floatingToolbarId,\n }\n : {},\n [\n mentionDraft,\n suggestionsListId,\n suggestionsListItemId,\n selectedMentionSuggestionUserId,\n hasFloatingToolbarRange,\n floatingToolbarId,\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 useLayoutEffect(() => {\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 useLayoutEffect(() => {\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 {...additionalProps}\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 {hasResolveMentionSuggestions && (\n <ComposerEditorMentionSuggestionsWrapper\n dir={dir}\n mentionDraft={mentionDraft}\n setMentionDraft={setMentionDraft}\n selectedUserId={selectedMentionSuggestionUserId}\n setSelectedUserId={setSelectedMentionSuggestionUserId}\n userIds={mentionSuggestions}\n id={suggestionsListId}\n itemId={suggestionsListItemId}\n onItemSelect={createMention}\n MentionSuggestions={MentionSuggestions}\n />\n )}\n {FloatingToolbar && (\n <ComposerEditorFloatingToolbarWrapper\n dir={dir}\n id={floatingToolbarId}\n hasFloatingToolbarRange={hasFloatingToolbarRange}\n setHasFloatingToolbarRange={setHasFloatingToolbarRange}\n FloatingToolbar={FloatingToolbar}\n />\n )}\n </Slate>\n );\n }\n);\n\nconst MAX_ATTACHMENTS = 10;\nconst MAX_ATTACHMENT_SIZE = 1024 * 1024 * 1024; // 1 GB\n\nfunction prepareAttachment(file: File): CommentLocalAttachment {\n return {\n type: \"localAttachment\",\n status: \"idle\",\n id: createCommentAttachmentId(),\n name: file.name,\n size: file.size,\n mimeType: file.type,\n file,\n };\n}\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 preventUnsavedChanges = true,\n disabled,\n asChild,\n roomId: _roomId,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"form\";\n const [isEmpty, setEmpty] = useState(true);\n const [isSubmitting, setSubmitting] = useState(false);\n const [isFocused, setFocused] = useState(false);\n const room = useRoomOrNull();\n\n const roomId = _roomId !== undefined ? _roomId : room?.id;\n if (roomId === undefined) {\n throw new Error(\"Composer.Form must be a descendant of RoomProvider.\");\n }\n\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\n const {\n attachments,\n isUploadingAttachments,\n addAttachments,\n removeAttachment,\n clearAttachments,\n } = useComposerAttachmentsManager(defaultAttachments, {\n maxFileSize: maxAttachmentSize,\n roomId,\n });\n const numberOfAttachments = attachments.length;\n const hasMaxAttachments = numberOfAttachments >= maxAttachments;\n\n const isDisabled = useMemo(() => {\n return isSubmitting || disabled === true;\n }, [isSubmitting, disabled]);\n const canSubmit = useMemo(() => {\n return !isEmpty && !isUploadingAttachments;\n }, [isEmpty, isUploadingAttachments]);\n const [marks, setMarks] = useState<ComposerBodyMarks>(getMarks);\n\n const ref = useRef<HTMLFormElement>(null);\n const mergedRefs = useRefs(forwardedRef, ref);\n const fileInputRef = useRef<HTMLInputElement>(null);\n const syncSource = useSyncSource();\n\n // Mark the composer as a pending update when it has unsubmitted (draft)\n // text or attachments\n const isPending = !preventUnsavedChanges\n ? false\n : !isEmpty || isUploadingAttachments || attachments.length > 0;\n\n useEffect(() => {\n syncSource?.setSyncStatus(\n isPending ? \"has-local-changes\" : \"synchronized\"\n );\n }, [syncSource, isPending]);\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) => prepareAttachment(file));\n\n addAttachments(attachments);\n },\n [addAttachments, maxAttachments, numberOfAttachments]\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 const onEditorChange = useInitial(makeEventSource) as EventSource<void>;\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, SlateEditor.end(editor, []));\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 const toggleMark = useCallback(\n (mark: ComposerBodyMark) => {\n toggleEditorMark(editor, mark);\n },\n [editor]\n );\n\n useObservable(onEditorChange, () => {\n setMarks(getMarks(editor));\n });\n\n return (\n <ComposerEditorContext.Provider\n value={{\n editor,\n validate,\n setFocused,\n onEditorChange,\n roomId,\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 toggleMark,\n marks,\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\n/**\n * A toggle button which toggles a specific text mark.\n *\n * @example\n * <Composer.MarkToggle mark=\"bold\">\n * Bold\n * </Composer.MarkToggle>\n */\nconst ComposerMarkToggle = forwardRef<\n HTMLButtonElement,\n ComposerMarkToggleProps\n>(\n (\n { children, mark, onValueChange, onClick, asChild, ...props },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"button\";\n const { marks, toggleMark } = useComposer();\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n toggleMark(mark);\n onValueChange?.(mark);\n }\n },\n [mark, onClick, onValueChange, toggleMark]\n );\n\n return (\n <TogglePrimitive.Root\n asChild\n pressed={marks[mark]}\n onClick={handleClick}\n {...props}\n >\n <Component {...props} ref={forwardedRef}>\n {children}\n </Component>\n </TogglePrimitive.Root>\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 ComposerFloatingToolbar.displayName = COMPOSER_FLOATING_TOOLBAR_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 ComposerMarkToggle.displayName = COMPOSER_MARK_TOGGLE_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 ComposerFloatingToolbar as FloatingToolbar,\n ComposerForm as Form,\n ComposerLink as Link,\n ComposerMarkToggle as MarkToggle,\n ComposerMention as Mention,\n ComposerSubmit as Submit,\n ComposerSuggestions as Suggestions,\n ComposerSuggestionsList as SuggestionsList,\n ComposerSuggestionsListItem as SuggestionsListItem,\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAqJA;AAEA;AAEA;AACA;AACA;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;AACP;AACE;AAAwB;AAAI;AAC3B;AACH;AAAA;AAGP;AAEA;AAAmC;AACjC;AACA;AACA;AAEF;AACE;AAAa;AACiC;AAChC;AAGd;AACG;AAAS;AACP;AAAK;AAAa;AAAS;AAGlC;AAEA;AAAiD;AAC/C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACW;AACX;AAEF;AACE;AACA;AACA;AACA;AACA;AACA;AAEA;AAAM;AAC8B;AAClC;AACA;AACA;AACA;AACA;AACyB;AACzB;AACA;AACW;AACL;AAGR;AACE;AAAkD;AAGpD;AACE;AACE;AAEA;AAAA;AAGF;AACA;AAA6B;AAG/B;AACG;AAEI;AACQ;AACL;AACA;AACe;AACG;AAClB;AACA;AACA;AACK;AACP;AAEC;AACM;AACM;AACJ;AACK;AACL;AACC;AAGF;AACM;AACF;AACV;AAEC;AACC;AACA;AACF;AACF;AAEA;AAGV;AAEA;AAA8C;AAC5C;AACW;AACX;AACA;AACA;AAEF;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAM;AAC8B;AAClC;AACA;AACA;AACA;AACA;AACyB;AACnB;AACN;AACA;AACW;AACL;AAGR;AACE;AACE;AAAA;AAGF;AACA;AAEA;AACA;AAEA;AACE;AACA;AAAyD;AAC3D;AAGF;AAEE;AAGA;AACE;AAGA;AAME;AACA;AAAiB;AAEjB;AAEA;AACA;AAAqB;AACvB;AACD;AAGH;AACG;AAEI;AACQ;AACL;AACA;AACA;AACK;AACP;AAEC;AACM;AACM;AACJ;AACK;AACL;AACC;AAGF;AACM;AACF;AACV;AAEiB;AACnB;AAEA;AAGV;AAWM;AAIJ;AACA;AACA;AAAM;AACJ;AACK;AACL;AACA;AAEF;AACA;AAAsB;AACgC;AAC1C;AAEZ;AACA;AAEA;AAA0B;AAEtB;AAEA;AACA;AAAsB;AACxB;AACc;AAGhB;AACG;AACC;AACK;AACL;AACW;AACP;AACW;AACkB;AACtB;AACC;AACL;AACI;AACM;AACL;AACC;AACR;AACL;AACK;AAEJ;AAGP;AAEA;AAA+B;AAC7B;AACA;AAEF;AAEE;AAEA;AAAsB;AAElB;AACG;AACC;AACK;AACP;AAEC;AAEH;AACG;AACC;AACK;AAGP;AAGF;AACG;AAAM;AAA0C;AAC9C;AACH;AAGF;AAAO;AAEb;AAGA;AACE;AACE;AAAY;AAAQ;AAAS;AAG/B;AACE;AAAY;AAAI;AAAS;AAG3B;AACE;AAAY;AAAG;AAAS;AAG1B;AACE;AAAY;AAAM;AAAS;AAG7B;AAAQ;AAAS;AAAa;AAChC;AAEA;AAAmC;AACjC;AAEF;AACE;AAEA;AACG;AAAS;AAAY;AAA+B;AAClD;AAGP;AAQA;AAAwB;AAEpB;AACA;AAEA;AACG;AAC8B;AACzB;AACC;AAEJ;AACH;AAGN;AAQA;AAAqB;AAEjB;AAEA;AACG;AACQ;AACH;AACA;AACC;AAEJ;AACH;AAGN;AAKM;AAIJ;AACA;AACA;AAAM;AACC;AACL;AACA;AAEF;AACA;AAAsB;AACgC;AAC1C;AAEZ;AACA;AAEA;AACG;AACC;AACI;AAC6B;AACtB;AACC;AACL;AACI;AACM;AACJ;AACA;AACR;AACL;AACK;AAEJ;AAGP;AAcM;AAIJ;AACA;AAEA;AACG;AACM;AACL;AACW;AACP;AACC;AAEJ;AAGP;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;AAEJ;AACH;AAGN;AAEA;AAA0D;AAEtD;AAAQ;AAAa;AAAa;AAAS;AAC7C;AAEE;AACG;AACE;AAAA;AACA;AAAA;AACH;AAEJ;AAEE;AACG;AACE;AAEI;AAAgD;AAC9C;AAEJ;AACH;AAEA;AAER;AAQA;AAAuB;AAEnB;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AAEA;AAAM;AACJ;AACA;AACA;AACA;AACA;AACY;AACZ;AAEF;AACA;AACA;AACE;AAA4C;AAE9C;AAA+D;AACV;AACxC;AAGb;AAIA;AACA;AAGA;AACA;AAA2B;AACzB;AACc;AAEhB;AAAM;AACJ;AACA;AACA;AACA;AAEF;AACA;AAA0B;AACa;AAClC;AAEL;AAA0B;AACa;AAClC;AAEL;AAA8B;AAEoC;AAC7D;AAGL;AAAsB;AAElB;AACG;AAAsB;AAAkB;AAAgB;AAAO;AAEpE;AACc;AAGhB;AAAqB;AAEjB;AAKA;AAAsB;AACxB;AACyB;AAG3B;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;AAEA;AAEE;AACE;AACA;AAAgC;AAClC;AAIF;AACE;AAAK;AAIP;AAEE;AAEA;AACE;AAAO;AACT;AAIF;AACE;AACA;AAAmB;AAIrB;AACE;AACA;AAA+B;AAIjC;AACE;AACA;AAAiC;AAInC;AACE;AACA;AAAwC;AAI1C;AACE;AACA;AAA+B;AACjC;AACF;AACF;AACA;AACE;AACA;AACA;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;AAAwC;AAGhC;AACQ;AACe;AACJ;AACA;AACQ;AACvB;AACF;AAGA;AACmB;AACA;AAElB;AACT;AACE;AACA;AACA;AACA;AACA;AACA;AACF;AAGF;AACE;AAA2C;AAI7C;AACE;AACE;AAAM;AACR;AAKF;AACE;AACE;AAAO;AACT;AAGF;AACG;AACC;AACc;AACJ;AAEV;AAAC;AACC;AACuC;AACxB;AACJ;AACgB;AACE;AACzB;AACA;AACM;AACA;AACC;AACF;AACD;AACR;AACY;AACO;AACrB;AAEG;AACC;AACA;AACA;AACgB;AACG;AACV;AACL;AACI;AACM;AACd;AACF;AAGC;AACC;AACI;AACJ;AACA;AACA;AACF;AAAA;AAEJ;AAGN;AAEA;AACA;AAEA;AACE;AAAO;AACC;AACE;AACsB;AACnB;AACA;AACI;AACf;AAEJ;AAWA;AAAqB;AAEjB;AACE;AACA;AACA;AACsB;AACtB;AACwB;AACxB;AACA;AACQ;AACL;AAIL;AACA;AACA;AACA;AACA;AAEA;AACA;AACE;AAAqE;AAIvE;AACA;AAEA;AAAM;AACJ;AACA;AACA;AACA;AACA;AACoD;AACvC;AACb;AAEF;AACA;AAEA;AACE;AAAoC;AAEtC;AACE;AAAoB;AAEtB;AAEA;AACA;AACA;AACA;AAIA;AAIA;AACE;AAAY;AACwB;AACpC;AAGF;AAA0B;AAEtB;AACE;AAAA;AAGF;AAAmC;AACjC;AACiB;AAGnB;AAEA;AAEA;AAA0B;AAC5B;AACoD;AAGtD;AAEA;AACE;AAA+B;AAGjC;AACE;AAAkC;AAGpC;AAAe;AACQ;AACA;AACnB;AACD;AAEH;AAEA;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;AAA0D;AAG5D;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;AAAmB;AAEf;AAA6B;AAC/B;AACO;AAGT;AACE;AAAyB;AAG3B;AACG;AACQ;AACL;AACA;AACA;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;AACA;AACA;AACF;AAEC;AAAc;AAAiB;AAAmB;AACjD;AAAC;AACM;AACG;AACH;AACK;AACD;AACC;AACe;AAC3B;AACC;AAAW;AAAS;AAAA;AACvB;AACF;AACF;AACF;AAGN;AAQA;AAAuB;AAEnB;AACA;AACA;AAEA;AACG;AACM;AACD;AACC;AACK;AAET;AACH;AAGN;AAQM;AAIJ;AACA;AACA;AACA;AAEA;AAAoB;AAEhB;AAEA;AACE;AAAY;AACd;AACF;AACqB;AAGvB;AACG;AACM;AACD;AACK;AACJ;AACK;AAET;AAGP;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;AAUA;AAA2B;AAQvB;AACA;AAEA;AAAoB;AAEhB;AAEA;AACE;AACA;AAAoB;AACtB;AACF;AACyC;AAG3C;AACG;AACQ;AACQ;AACN;AACL;AAEH;AAAc;AAAY;AACxB;AACH;AACF;AAGN;AAEA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;;;;;;;;;;;;;"}
@@ -660,7 +660,6 @@ const ComposerEditor = forwardRef(
660
660
  }
661
661
  }
662
662
  if (isKey(event, "Escape")) {
663
- event.preventDefault();
664
663
  blur();
665
664
  }
666
665
  if (isKey(event, "Enter", { shift: false })) {