{"version":3,"file":"index.mjs","sources":["../../../src/primitives/Composer/index.tsx"],"sourcesContent":["\"use client\";\n\nimport type {\n  DetectOverflowOptions,\n  UseFloatingOptions,\n} from \"@floating-ui/react-dom\";\nimport {\n  autoUpdate,\n  flip,\n  hide,\n  limitShift,\n  shift,\n  size,\n  useFloating,\n} from \"@floating-ui/react-dom\";\nimport { type CommentBody, kInternal } from \"@liveblocks/core\";\nimport { useRoomContextBundle } from \"@liveblocks/react\";\nimport { Slot } from \"@radix-ui/react-slot\";\nimport type {\n  AriaAttributes,\n  FocusEvent,\n  FormEvent,\n  KeyboardEvent,\n  PointerEvent,\n} from \"react\";\nimport React, {\n  forwardRef,\n  useCallback,\n  useEffect,\n  useImperativeHandle,\n  useMemo,\n  useRef,\n  useState,\n} from \"react\";\nimport type {\n  Descendant as SlateDescendant,\n  Element as SlateElement,\n} from \"slate\";\nimport {\n  createEditor,\n  Editor as SlateEditor,\n  insertText as insertSlateText,\n  Transforms as SlateTransforms,\n} from \"slate\";\nimport { withHistory } from \"slate-history\";\nimport type {\n  RenderElementProps,\n  RenderElementSpecificProps,\n  RenderLeafProps,\n  RenderPlaceholderProps,\n} from \"slate-react\";\nimport {\n  Editable,\n  ReactEditor,\n  Slate,\n  useSelected,\n  useSlateStatic,\n  withReact,\n} from \"slate-react\";\n\nimport { useCommentsConfig } from \"../../config\";\nimport { FLOATING_ELEMENT_COLLISION_PADDING } from \"../../constants\";\nimport { withAutoFormatting } from \"../../slate/plugins/auto-formatting\";\nimport { withAutoLinks } from \"../../slate/plugins/auto-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 { getDOMRange } from \"../../slate/utils/get-dom-range\";\nimport { isEmpty as isEditorEmpty } from \"../../slate/utils/is-empty\";\nimport { leaveMarkEdge, toggleMark } from \"../../slate/utils/marks\";\nimport type {\n  ComposerBody as ComposerBodyData,\n  ComposerBodyAutoLink,\n  ComposerBodyMention,\n} from \"../../types\";\nimport { isKey } from \"../../utils/is-key\";\nimport { Persist, useAnimationPersist, usePersist } from \"../../utils/Persist\";\nimport { Portal } from \"../../utils/Portal\";\nimport { requestSubmit } from \"../../utils/request-submit\";\nimport { useId } from \"../../utils/use-id\";\nimport { useIndex } from \"../../utils/use-index\";\nimport { useInitial } from \"../../utils/use-initial\";\nimport { useLayoutEffect } from \"../../utils/use-layout-effect\";\nimport { useRefs } from \"../../utils/use-refs\";\nimport { toAbsoluteUrl } from \"../Comment/utils\";\nimport {\n  ComposerContext,\n  ComposerEditorContext,\n  ComposerSuggestionsContext,\n  useComposer,\n  useComposerEditorContext,\n  useComposerSuggestionsContext,\n} from \"./contexts\";\nimport type {\n  ComposerEditorComponents,\n  ComposerEditorElementProps,\n  ComposerEditorLinkWrapperProps,\n  ComposerEditorMentionSuggestionsWrapperProps,\n  ComposerEditorMentionWrapperProps,\n  ComposerEditorProps,\n  ComposerFormProps,\n  ComposerLinkProps,\n  ComposerMentionProps,\n  ComposerSubmitProps,\n  ComposerSuggestionsListItemProps,\n  ComposerSuggestionsListProps,\n  ComposerSuggestionsProps,\n  SuggestionsPosition,\n} from \"./types\";\nimport {\n  commentBodyToComposerBody,\n  composerBodyToCommentBody,\n  getPlacementFromPosition,\n  getSideAndAlignFromPlacement,\n} from \"./utils\";\n\nconst MENTION_SUGGESTIONS_POSITION: SuggestionsPosition = \"top\";\n\nconst COMPOSER_MENTION_NAME = \"ComposerMention\";\nconst COMPOSER_LINK_NAME = \"ComposerLink\";\nconst COMPOSER_SUGGESTIONS_NAME = \"ComposerSuggestions\";\nconst COMPOSER_SUGGESTIONS_LIST_NAME = \"ComposerSuggestionsList\";\nconst COMPOSER_SUGGESTIONS_LIST_ITEM_NAME = \"ComposerSuggestionsListItem\";\nconst COMPOSER_SUBMIT_NAME = \"ComposerSubmit\";\nconst COMPOSER_EDITOR_NAME = \"ComposerEditor\";\nconst COMPOSER_FORM_NAME = \"ComposerForm\";\n\nconst emptyCommentBody: CommentBody = {\n  version: 1,\n  content: [{ type: \"paragraph\", children: [{ text: \"\" }] }],\n};\n\nfunction createComposerEditor() {\n  return withAutoLinks(\n    withMentions(\n      withEmptyClearFormatting(\n        withAutoFormatting(withHistory(withReact(createEditor())))\n      )\n    )\n  );\n}\n\nfunction ComposerEditorMentionWrapper({\n  Mention,\n  attributes,\n  children,\n  element,\n}: ComposerEditorMentionWrapperProps) {\n  const isSelected = useSelected();\n\n  return (\n    <span {...attributes}>\n      {element.id ? (\n        <Mention userId={element.id} isSelected={isSelected} />\n      ) : null}\n      {children}\n    </span>\n  );\n}\n\nfunction ComposerEditorLinkWrapper({\n  Link,\n  attributes,\n  element,\n  children,\n}: ComposerEditorLinkWrapperProps) {\n  const href = useMemo(\n    () => toAbsoluteUrl(element.url) ?? element.url,\n    [element.url]\n  );\n\n  return (\n    <span {...attributes}>\n      <Link href={href}>{children}</Link>\n    </span>\n  );\n}\n\nfunction ComposerEditorMentionSuggestionsWrapper({\n  id,\n  itemId,\n  userIds,\n  selectedUserId,\n  setSelectedUserId,\n  mentionDraft,\n  onItemSelect,\n  position = MENTION_SUGGESTIONS_POSITION,\n  dir,\n  MentionSuggestions,\n}: ComposerEditorMentionSuggestionsWrapperProps) {\n  const editor = useSlateStatic();\n  const { isFocused } = useComposer();\n  const [content, setContent] = useState<HTMLDivElement | null>(null);\n  const [contentZIndex, setContentZIndex] = useState<string>();\n  const contentRef = useCallback(setContent, [setContent]);\n  const { portalContainer } = useCommentsConfig();\n  const floatingOptions: UseFloatingOptions = useMemo(() => {\n    const detectOverflowOptions: DetectOverflowOptions = {\n      padding: FLOATING_ELEMENT_COLLISION_PADDING,\n    };\n\n    return {\n      strategy: \"fixed\",\n      placement: getPlacementFromPosition(position, dir),\n      middleware: [\n        flip({ ...detectOverflowOptions, crossAxis: false }),\n        hide(detectOverflowOptions),\n        shift({\n          ...detectOverflowOptions,\n          limiter: limitShift(),\n        }),\n        size({\n          ...detectOverflowOptions,\n          apply({ availableWidth, availableHeight, elements }) {\n            elements.floating.style.setProperty(\n              \"--lb-composer-suggestions-available-width\",\n              `${availableWidth}px`\n            );\n            elements.floating.style.setProperty(\n              \"--lb-composer-suggestions-available-height\",\n              `${availableHeight}px`\n            );\n          },\n        }),\n      ],\n      whileElementsMounted: autoUpdate,\n    };\n  }, [position, dir]);\n  const {\n    refs: { setReference, setFloating },\n    strategy,\n    isPositioned,\n    placement,\n    x,\n    y,\n  } = useFloating(floatingOptions);\n\n  // Copy `z-index` from content to wrapper.\n  // Inspired by https://github.com/radix-ui/primitives/blob/main/packages/react/popper/src/Popper.tsx\n  useLayoutEffect(() => {\n    if (content) {\n      setContentZIndex(window.getComputedStyle(content).zIndex);\n    }\n  }, [content]);\n\n  useLayoutEffect(() => {\n    if (!mentionDraft) {\n      return;\n    }\n\n    const domRange = getDOMRange(editor, mentionDraft.range);\n\n    if (domRange) {\n      setReference({\n        getBoundingClientRect: () => domRange.getBoundingClientRect(),\n        getClientRects: () => domRange.getClientRects(),\n      });\n    }\n  }, [setReference, editor, mentionDraft]);\n\n  return (\n    <Persist>\n      {mentionDraft?.range && isFocused && userIds ? (\n        <ComposerSuggestionsContext.Provider\n          value={{\n            id,\n            itemId,\n            selectedValue: selectedUserId,\n            setSelectedValue: setSelectedUserId,\n            onItemSelect,\n            placement,\n            dir,\n            ref: contentRef,\n          }}\n        >\n          <Portal\n            ref={setFloating}\n            container={portalContainer}\n            style={{\n              position: strategy,\n              top: 0,\n              left: 0,\n              transform: isPositioned\n                ? `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`\n                : \"translate3d(0, -200%, 0)\",\n              minWidth: \"max-content\",\n              zIndex: contentZIndex,\n            }}\n          >\n            <MentionSuggestions\n              userIds={userIds}\n              selectedUserId={selectedUserId}\n            />\n          </Portal>\n        </ComposerSuggestionsContext.Provider>\n      ) : null}\n    </Persist>\n  );\n}\n\nfunction ComposerEditorElement({\n  Mention,\n  Link,\n  ...props\n}: ComposerEditorElementProps) {\n  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n  const { attributes, children, element } = props;\n\n  switch (element.type) {\n    case \"mention\":\n      return (\n        <ComposerEditorMentionWrapper\n          Mention={Mention}\n          {...(props as RenderElementSpecificProps<ComposerBodyMention>)}\n        />\n      );\n    case \"auto-link\":\n      return (\n        <ComposerEditorLinkWrapper\n          Link={Link}\n          {...(props as RenderElementSpecificProps<ComposerBodyAutoLink>)}\n        />\n      );\n    case \"paragraph\":\n      return (\n        <p {...attributes} style={{ position: \"relative\" }}>\n          {children}\n        </p>\n      );\n    default:\n      return null;\n  }\n}\n\n// <code><s><em><strong>text</strong></s></em></code>\nfunction ComposerEditorLeaf({ attributes, children, leaf }: RenderLeafProps) {\n  if (leaf.bold) {\n    children = <strong>{children}</strong>;\n  }\n\n  if (leaf.italic) {\n    children = <em>{children}</em>;\n  }\n\n  if (leaf.strikethrough) {\n    children = <s>{children}</s>;\n  }\n\n  if (leaf.code) {\n    children = <code>{children}</code>;\n  }\n\n  return <span {...attributes}>{children}</span>;\n}\n\nfunction ComposerEditorPlaceholder({\n  attributes,\n  children,\n}: RenderPlaceholderProps) {\n  const { opacity: _opacity, ...style } = attributes.style;\n\n  return (\n    <span {...attributes} style={style} data-placeholder=\"\">\n      {children}\n    </span>\n  );\n}\n\n/**\n * Displays mentions within `Composer.Editor`.\n *\n * @example\n * <Composer.Mention>@{userId}</Composer.Mention>\n */\nconst ComposerMention = forwardRef<HTMLSpanElement, ComposerMentionProps>(\n  ({ children, asChild, ...props }, forwardedRef) => {\n    const Component = asChild ? Slot : \"span\";\n    const isSelected = useSelected();\n\n    return (\n      <Component\n        data-selected={isSelected || undefined}\n        {...props}\n        ref={forwardedRef}\n      >\n        {children}\n      </Component>\n    );\n  }\n);\n\n/**\n * Displays links within `Composer.Editor`.\n *\n * @example\n * <Composer.Link href={href}>{children}</Composer.Link>\n */\nconst ComposerLink = forwardRef<HTMLAnchorElement, ComposerLinkProps>(\n  ({ children, asChild, ...props }, forwardedRef) => {\n    const Component = asChild ? Slot : \"a\";\n\n    return (\n      <Component\n        target=\"_blank\"\n        rel=\"noopener noreferrer nofollow\"\n        {...props}\n        ref={forwardedRef}\n      >\n        {children}\n      </Component>\n    );\n  }\n);\n\n/**\n * Contains suggestions within `Composer.Editor`.\n */\nconst ComposerSuggestions = forwardRef<\n  HTMLDivElement,\n  ComposerSuggestionsProps\n>(({ children, style, asChild, ...props }, forwardedRef) => {\n  const [isPresent] = usePersist();\n  const ref = useRef<HTMLDivElement>(null);\n  const {\n    ref: contentRef,\n    placement,\n    dir,\n  } = useComposerSuggestionsContext(COMPOSER_SUGGESTIONS_NAME);\n  const mergedRefs = useRefs(forwardedRef, contentRef, ref);\n  const [side, align] = useMemo(\n    () => getSideAndAlignFromPlacement(placement),\n    [placement]\n  );\n  const Component = asChild ? Slot : \"div\";\n  useAnimationPersist(ref);\n\n  return (\n    <Component\n      dir={dir}\n      {...props}\n      data-state={isPresent ? \"open\" : \"closed\"}\n      data-side={side}\n      data-align={align}\n      style={{\n        display: \"flex\",\n        flexDirection: \"column\",\n        maxHeight: \"var(--lb-composer-suggestions-available-height)\",\n        overflowY: \"auto\",\n        ...style,\n      }}\n      ref={mergedRefs}\n    >\n      {children}\n    </Component>\n  );\n});\n\n/**\n * Displays a list of suggestions within `Composer.Editor`.\n *\n * @example\n * <Composer.SuggestionsList>\n *   {userIds.map((userId) => (\n *     <Composer.SuggestionsListItem key={userId} value={userId}>\n *       @{userId}\n *     </Composer.SuggestionsListItem>\n *   ))}\n * </Composer.SuggestionsList>\n */\nconst ComposerSuggestionsList = forwardRef<\n  HTMLUListElement,\n  ComposerSuggestionsListProps\n>(({ children, asChild, ...props }, forwardedRef) => {\n  const { id } = useComposerSuggestionsContext(COMPOSER_SUGGESTIONS_LIST_NAME);\n  const Component = asChild ? Slot : \"ul\";\n\n  return (\n    <Component\n      role=\"listbox\"\n      id={id}\n      aria-label=\"Suggestions list\"\n      {...props}\n      ref={forwardedRef}\n    >\n      {children}\n    </Component>\n  );\n});\n\n/**\n * Displays a suggestion within `Composer.SuggestionsList`.\n *\n * @example\n * <Composer.SuggestionsListItem key={userId} value={userId}>\n *   @{userId}\n * </Composer.SuggestionsListItem>\n */\nconst ComposerSuggestionsListItem = forwardRef<\n  HTMLLIElement,\n  ComposerSuggestionsListItemProps\n>(\n  (\n    { value, children, onPointerMove, onPointerDown, asChild, ...props },\n    forwardedRef\n  ) => {\n    const ref = useRef<HTMLLIElement>(null);\n    const mergedRefs = useRefs(forwardedRef, ref);\n    const { selectedValue, setSelectedValue, itemId, onItemSelect } =\n      useComposerSuggestionsContext(COMPOSER_SUGGESTIONS_LIST_ITEM_NAME);\n    const Component = asChild ? Slot : \"li\";\n    const isSelected = useMemo(\n      () => selectedValue === value,\n      [selectedValue, value]\n    );\n    // TODO: Support props.id if provided, it will need to be sent up to Composer.Editor to use it in aria-activedescendant\n    const id = useMemo(() => itemId(value), [itemId, value]);\n\n    useEffect(() => {\n      if (ref?.current && isSelected) {\n        ref.current.scrollIntoView({ block: \"nearest\" });\n      }\n    }, [isSelected]);\n\n    const handlePointerMove = useCallback(\n      (event: PointerEvent<HTMLLIElement>) => {\n        onPointerMove?.(event);\n\n        if (!event.isDefaultPrevented()) {\n          setSelectedValue(value);\n        }\n      },\n      [onPointerMove, setSelectedValue, value]\n    );\n\n    const handlePointerDown = useCallback(\n      (event: PointerEvent<HTMLLIElement>) => {\n        onPointerDown?.(event);\n\n        if (!event.isDefaultPrevented()) {\n          const target = event.target as HTMLElement;\n\n          if (target.hasPointerCapture(event.pointerId)) {\n            target.releasePointerCapture(event.pointerId);\n          }\n\n          if (event.button === 0 && event.ctrlKey === false) {\n            onItemSelect(value);\n\n            event.preventDefault();\n          }\n        }\n      },\n      [onItemSelect, onPointerDown, value]\n    );\n\n    return (\n      <Component\n        role=\"option\"\n        id={id}\n        data-selected={isSelected || undefined}\n        aria-selected={isSelected || undefined}\n        onPointerMove={handlePointerMove}\n        onPointerDown={handlePointerDown}\n        {...props}\n        ref={mergedRefs}\n      >\n        {children}\n      </Component>\n    );\n  }\n);\n\nconst defaultEditorComponents: ComposerEditorComponents = {\n  Link: ({ href, children }) => {\n    return <ComposerLink href={href}>{children}</ComposerLink>;\n  },\n  Mention: ({ userId }) => {\n    return (\n      <ComposerMention>\n        {MENTION_CHARACTER}\n        {userId}\n      </ComposerMention>\n    );\n  },\n  MentionSuggestions: ({ userIds }) => {\n    return userIds.length > 0 ? (\n      <ComposerSuggestions>\n        <ComposerSuggestionsList>\n          {userIds.map((userId) => (\n            <ComposerSuggestionsListItem key={userId} value={userId}>\n              {userId}\n            </ComposerSuggestionsListItem>\n          ))}\n        </ComposerSuggestionsList>\n      </ComposerSuggestions>\n    ) : null;\n  },\n};\n\n/**\n * Displays the composer's editor.\n *\n * @example\n * <Composer.Editor placeholder=\"Write a comment…\" />\n */\nconst ComposerEditor = forwardRef<HTMLDivElement, ComposerEditorProps>(\n  (\n    {\n      defaultValue,\n      onKeyDown,\n      onFocus,\n      onBlur,\n      disabled,\n      autoFocus,\n      components,\n      dir,\n      ...props\n    },\n    forwardedRef\n  ) => {\n    const {\n      [kInternal]: { useMentionSuggestions },\n      useSelf,\n    } = useRoomContextBundle();\n    const self = useSelf();\n    const isDisabled = useMemo(\n      () => disabled || !self?.canComment,\n      [disabled, self?.canComment]\n    );\n    const { editor, validate, setFocused } = useComposerEditorContext();\n    const { submit, focus, select, isEmpty, isFocused } = useComposer();\n    const initialBody = useInitial(defaultValue ?? emptyCommentBody);\n    const initialEditorValue = useMemo(() => {\n      return commentBodyToComposerBody(initialBody);\n    }, [initialBody]);\n    const { Link, Mention, MentionSuggestions } = useMemo(\n      () => ({ ...defaultEditorComponents, ...components }),\n      [components]\n    );\n\n    const [mentionDraft, setMentionDraft] = useState<MentionDraft>();\n    const mentionSuggestions = useMentionSuggestions(mentionDraft?.text);\n    const [\n      selectedMentionSuggestionIndex,\n      setPreviousSelectedMentionSuggestionIndex,\n      setNextSelectedMentionSuggestionIndex,\n      setSelectedMentionSuggestionIndex,\n    ] = useIndex(0, mentionSuggestions?.length ?? 0);\n    const id = useId();\n    const suggestionsListId = useMemo(\n      () => `liveblocks-suggestions-list-${id}`,\n      [id]\n    );\n    const suggestionsListItemId = useCallback(\n      (userId?: string) =>\n        userId ? `liveblocks-suggestions-list-item-${id}-${userId}` : undefined,\n      [id]\n    );\n    const renderElement = useCallback(\n      (props: RenderElementProps) => {\n        return (\n          <ComposerEditorElement Mention={Mention} Link={Link} {...props} />\n        );\n      },\n      [Link, Mention]\n    );\n\n    const handleChange = useCallback(\n      (value: SlateDescendant[]) => {\n        validate(value as SlateElement[]);\n\n        setMentionDraft(getMentionDraftAtSelection(editor));\n      },\n      [editor, validate]\n    );\n\n    const createMention = useCallback(\n      (userId?: string) => {\n        if (!mentionDraft || !userId) {\n          return;\n        }\n\n        SlateTransforms.select(editor, mentionDraft.range);\n        insertMention(editor, userId);\n        setMentionDraft(undefined);\n        setSelectedMentionSuggestionIndex(0);\n      },\n      [editor, mentionDraft, setSelectedMentionSuggestionIndex]\n    );\n\n    const handleKeyDown = useCallback(\n      (event: KeyboardEvent<HTMLDivElement>) => {\n        onKeyDown?.(event);\n\n        if (event.isDefaultPrevented()) {\n          return;\n        }\n\n        // Allow leaving marks with ArrowLeft\n        if (isKey(event, \"ArrowLeft\")) {\n          leaveMarkEdge(editor, \"start\");\n        }\n\n        // Allow leaving marks with ArrowRight\n        if (isKey(event, \"ArrowRight\")) {\n          leaveMarkEdge(editor, \"end\");\n        }\n\n        if (mentionDraft && mentionSuggestions?.length) {\n          // Select the next mention suggestion on ArrowDown\n          if (isKey(event, \"ArrowDown\")) {\n            event.preventDefault();\n            setNextSelectedMentionSuggestionIndex();\n          }\n\n          // Select the previous mention suggestion on ArrowUp\n          if (isKey(event, \"ArrowUp\")) {\n            event.preventDefault();\n            setPreviousSelectedMentionSuggestionIndex();\n          }\n\n          // Create a mention on Enter/Tab\n          if (isKey(event, \"Enter\") || isKey(event, \"Tab\")) {\n            event.preventDefault();\n\n            const userId = mentionSuggestions?.[selectedMentionSuggestionIndex];\n            createMention(userId);\n          }\n\n          // Close the suggestions on Escape\n          if (isKey(event, \"Escape\")) {\n            event.preventDefault();\n            setMentionDraft(undefined);\n            setSelectedMentionSuggestionIndex(0);\n          }\n        } else {\n          // Blur the editor on Escape\n          if (isKey(event, \"Escape\")) {\n            event.preventDefault();\n            ReactEditor.blur(editor);\n          }\n\n          // Submit the editor on Enter\n          if (isKey(event, \"Enter\", { shift: false }) && !isEmpty) {\n            event.preventDefault();\n            submit();\n          }\n\n          // Create a new line on Shift + Enter\n          if (isKey(event, \"Enter\", { shift: true })) {\n            event.preventDefault();\n            editor.insertBreak();\n          }\n\n          // Toggle bold on Command/Control + B\n          if (isKey(event, \"b\", { mod: true })) {\n            event.preventDefault();\n            toggleMark(editor, \"bold\");\n          }\n\n          // Toggle italic on Command/Control + I\n          if (isKey(event, \"i\", { mod: true })) {\n            event.preventDefault();\n            toggleMark(editor, \"italic\");\n          }\n\n          // Toggle strikethrough on Command/Control + Shift + S\n          if (isKey(event, \"s\", { mod: true, shift: true })) {\n            event.preventDefault();\n            toggleMark(editor, \"strikethrough\");\n          }\n\n          // Toggle code on Command/Control + E\n          if (isKey(event, \"e\", { mod: true })) {\n            event.preventDefault();\n            toggleMark(editor, \"code\");\n          }\n        }\n      },\n      [\n        createMention,\n        editor,\n        isEmpty,\n        mentionDraft,\n        mentionSuggestions,\n        selectedMentionSuggestionIndex,\n        onKeyDown,\n        setNextSelectedMentionSuggestionIndex,\n        setPreviousSelectedMentionSuggestionIndex,\n        setSelectedMentionSuggestionIndex,\n        submit,\n      ]\n    );\n\n    const handleFocus = useCallback(\n      (event: FocusEvent<HTMLDivElement>) => {\n        onFocus?.(event);\n\n        if (!event.isDefaultPrevented()) {\n          setFocused(true);\n        }\n      },\n      [onFocus, setFocused]\n    );\n\n    const handleBlur = useCallback(\n      (event: FocusEvent<HTMLDivElement>) => {\n        onBlur?.(event);\n\n        if (!event.isDefaultPrevented()) {\n          setFocused(false);\n        }\n      },\n      [onBlur, setFocused]\n    );\n\n    const selectedMentionSuggestionUserId = useMemo(\n      () => mentionSuggestions?.[selectedMentionSuggestionIndex],\n      [selectedMentionSuggestionIndex, mentionSuggestions]\n    );\n    const setSelectedMentionSuggestionUserId = useCallback(\n      (userId: string) => {\n        const index = mentionSuggestions?.indexOf(userId);\n\n        if (index !== undefined && index >= 0) {\n          setSelectedMentionSuggestionIndex(index);\n        }\n      },\n      [setSelectedMentionSuggestionIndex, mentionSuggestions]\n    );\n\n    const propsWhileSuggesting: AriaAttributes = useMemo(\n      () =>\n        mentionDraft\n          ? {\n              role: \"combobox\",\n              \"aria-autocomplete\": \"list\",\n              \"aria-expanded\": true,\n              \"aria-controls\": suggestionsListId,\n              \"aria-activedescendant\": suggestionsListItemId(\n                selectedMentionSuggestionUserId\n              ),\n            }\n          : {},\n      [\n        mentionDraft,\n        suggestionsListId,\n        suggestionsListItemId,\n        selectedMentionSuggestionUserId,\n      ]\n    );\n\n    useImperativeHandle(\n      forwardedRef,\n      () => {\n        return ReactEditor.toDOMNode(editor, editor) as HTMLDivElement;\n      },\n      [editor]\n    );\n\n    // Manually focus the editor when `autoFocus` is true\n    useEffect(() => {\n      if (autoFocus) {\n        focus();\n      }\n    }, [autoFocus, editor, focus]);\n\n    // Manually add a selection in the editor if the selection\n    // is still empty after being focused\n    useEffect(() => {\n      if (isFocused && editor.selection === null) {\n        select();\n      }\n    }, [editor, select, isFocused]);\n\n    return (\n      <Slate\n        editor={editor}\n        initialValue={initialEditorValue}\n        onChange={handleChange}\n      >\n        <Editable\n          dir={dir}\n          enterKeyHint={mentionDraft ? \"enter\" : \"send\"}\n          autoCapitalize=\"sentences\"\n          aria-label=\"Composer editor\"\n          data-focused={isFocused || undefined}\n          data-disabled={isDisabled || undefined}\n          {...propsWhileSuggesting}\n          {...props}\n          readOnly={isDisabled}\n          disabled={isDisabled}\n          onKeyDown={handleKeyDown}\n          onFocus={handleFocus}\n          onBlur={handleBlur}\n          renderElement={renderElement}\n          renderLeaf={ComposerEditorLeaf}\n          renderPlaceholder={ComposerEditorPlaceholder}\n        />\n        <ComposerEditorMentionSuggestionsWrapper\n          dir={dir}\n          mentionDraft={mentionDraft}\n          selectedUserId={selectedMentionSuggestionUserId}\n          setSelectedUserId={setSelectedMentionSuggestionUserId}\n          userIds={mentionSuggestions}\n          id={suggestionsListId}\n          itemId={suggestionsListItemId}\n          onItemSelect={createMention}\n          MentionSuggestions={MentionSuggestions}\n        />\n      </Slate>\n    );\n  }\n);\n\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    { children, onSubmit, onComposerSubmit, asChild, ...props },\n    forwardedRef\n  ) => {\n    const Component = asChild ? Slot : \"form\";\n    const editor = useInitial(createComposerEditor);\n    const [isEmpty, setEmpty] = useState(true);\n    const [isFocused, setFocused] = useState(false);\n    const ref = useRef<HTMLFormElement>(null);\n    const mergedRefs = useRefs(forwardedRef, ref);\n\n    const validate = useCallback(\n      (value: SlateElement[]) => {\n        setEmpty(isEditorEmpty(editor, value));\n      },\n      [editor]\n    );\n\n    const submit = useCallback(() => {\n      if (ref.current) {\n        requestSubmit(ref.current);\n      }\n    }, []);\n\n    const clear = useCallback(() => {\n      SlateTransforms.delete(editor, {\n        at: {\n          anchor: SlateEditor.start(editor, []),\n          focus: SlateEditor.end(editor, []),\n        },\n      });\n    }, [editor]);\n\n    const select = useCallback(() => {\n      SlateTransforms.select(editor, {\n        anchor: SlateEditor.end(editor, []),\n        focus: SlateEditor.end(editor, []),\n      });\n    }, [editor]);\n\n    const focus = useCallback(\n      (resetSelection = true) => {\n        if (!ReactEditor.isFocused(editor)) {\n          SlateTransforms.select(\n            editor,\n            resetSelection || !editor.selection\n              ? SlateEditor.end(editor, [])\n              : editor.selection\n          );\n          ReactEditor.focus(editor);\n        }\n      },\n      [editor]\n    );\n\n    const blur = useCallback(() => {\n      ReactEditor.blur(editor);\n    }, [editor]);\n\n    const onSubmitEnd = useCallback(() => {\n      clear();\n      blur();\n    }, [blur, clear]);\n\n    const createMention = useCallback(() => {\n      focus();\n      insertMentionCharacter(editor);\n    }, [editor, focus]);\n\n    const insertText = useCallback(\n      (text: string) => {\n        focus(false);\n        insertSlateText(editor, text);\n      },\n      [editor, focus]\n    );\n\n    const handleSubmit = useCallback(\n      (event: FormEvent<HTMLFormElement>) => {\n        onSubmit?.(event);\n\n        if (event.isDefaultPrevented()) {\n          return;\n        }\n\n        const body = composerBodyToCommentBody(\n          editor.children as ComposerBodyData\n        );\n        const comment = { body };\n\n        const promise = onComposerSubmit?.(comment, event);\n\n        event.preventDefault();\n\n        if (promise) {\n          promise.then(onSubmitEnd);\n        } else {\n          onSubmitEnd();\n        }\n      },\n      [editor.children, onComposerSubmit, onSubmit, onSubmitEnd]\n    );\n\n    return (\n      <ComposerEditorContext.Provider\n        value={{\n          editor,\n          validate,\n          setFocused,\n        }}\n      >\n        <ComposerContext.Provider\n          value={{\n            isFocused,\n            isEmpty,\n            submit,\n            clear,\n            select,\n            focus,\n            blur,\n            createMention,\n            insertText,\n          }}\n        >\n          <Component {...props} onSubmit={handleSubmit} ref={mergedRefs}>\n            {children}\n          </Component>\n        </ComposerContext.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 { useSelf } = useRoomContextBundle();\n    const { isEmpty } = useComposer();\n    const self = useSelf();\n    const isDisabled = useMemo(\n      () => disabled || isEmpty || !self?.canComment,\n      [disabled, isEmpty, self?.canComment]\n    );\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\nif (process.env.NODE_ENV !== \"production\") {\n  ComposerEditor.displayName = COMPOSER_EDITOR_NAME;\n  ComposerForm.displayName = COMPOSER_FORM_NAME;\n  ComposerMention.displayName = COMPOSER_MENTION_NAME;\n  ComposerLink.displayName = COMPOSER_LINK_NAME;\n  ComposerSubmit.displayName = COMPOSER_SUBMIT_NAME;\n  ComposerSuggestions.displayName = COMPOSER_SUGGESTIONS_NAME;\n  ComposerSuggestionsList.displayName = COMPOSER_SUGGESTIONS_LIST_NAME;\n  ComposerSuggestionsListItem.displayName = COMPOSER_SUGGESTIONS_LIST_ITEM_NAME;\n}\n\n// NOTE: Every export from this file will be available publicly as Composer.*\nexport {\n  ComposerEditor as Editor,\n  ComposerForm as Form,\n  ComposerLink as Link,\n  ComposerMention as Mention,\n  ComposerSubmit as Submit,\n  ComposerSuggestions as Suggestions,\n  ComposerSuggestionsList as SuggestionsList,\n  ComposerSuggestionsListItem as SuggestionsListItem,\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0HA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAAsC;AAC3B;AAEX;AAEA;AACE;AAAO;AACL;AACE;AAC2D;AAC3D;AACF;AAEJ;AAEA;AAAsC;AACpC;AACA;AACA;AAEF;AACE;AAEA;AACG;AAAS;AAEL;AAAwB;AAAI;AAKrC;AAEA;AAAmC;AACjC;AACA;AACA;AAEF;AACE;AAAa;AACiC;AAChC;AAGd;AACG;AAAS;AACP;AAAK;AAGZ;AAEA;AAAiD;AAC/C;AACA;AACA;AACA;AACA;AACA;AACA;AACW;AACX;AAEF;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACE;AAAqD;AAC1C;AAGX;AAAO;AACK;AACuC;AACrC;AACyC;AACzB;AACpB;AACD;AACiB;AACrB;AACI;AACA;AAED;AAAwB;AACtB;AACG;AAEL;AAAwB;AACtB;AACG;AACL;AACF;AACD;AACH;AACsB;AACxB;AAEF;AAAM;AAC8B;AAClC;AACA;AACA;AACA;AACA;AAKF;AACE;AACE;AAAwD;AAC1D;AAGF;AACE;AACE;AAAA;AAGF;AAEA;AACE;AAAa;AACiD;AACd;AAC/C;AACH;AAGF;AAGO;AACQ;AACL;AACA;AACe;AACG;AAClB;AACA;AACA;AACK;AACP;AAEC;AACM;AACM;AACJ;AACK;AACL;AACC;AAGF;AACM;AACF;AACV;AAEC;AACC;AACA;AAOd;AAEA;AAA+B;AAC7B;AACA;AAEF;AAEE;AAEA;AAAsB;AAElB;AACG;AACC;AACK;AACP;AAGF;AACG;AACC;AACK;AACP;AAGF;AACG;AAAM;AAA0C;AAEjD;AAGF;AAAO;AAEb;AAGA;AACE;AACE;AAA6B;AAG/B;AACE;AAAyB;AAG3B;AACE;AAAwB;AAG1B;AACE;AAA2B;AAG7B;AAAQ;AAAS;AACnB;AAEA;AAAmC;AACjC;AAEF;AACE;AAEA;AACG;AAAS;AAAY;AAA+B;AAIzD;AAQA;AAAwB;AAEpB;AACA;AAEA;AACG;AAC8B;AACzB;AACC;AAGP;AAGN;AAQA;AAAqB;AAEjB;AAEA;AACG;AACQ;AACH;AACA;AACC;AAGP;AAGN;AAKM;AAIJ;AACA;AACA;AAAM;AACC;AACL;AACA;AAEF;AACA;AAAsB;AACwB;AAClC;AAEZ;AACA;AAEA;AACG;AACC;AACI;AAC6B;AACtB;AACC;AACL;AACI;AACM;AACJ;AACA;AACR;AACL;AACK;AAKX;AAcM;AAIJ;AACA;AAEA;AACG;AACM;AACL;AACW;AACP;AACC;AAKX;AAUA;AAAoC;AAQhC;AACA;AACA;AAEA;AACA;AAAmB;AACO;AACH;AAGvB;AAEA;AACE;AACE;AAA+C;AACjD;AAGF;AAA0B;AAEtB;AAEA;AACE;AAAsB;AACxB;AACF;AACuC;AAGzC;AAA0B;AAEtB;AAEA;AACE;AAEA;AACE;AAA4C;AAG9C;AACE;AAEA;AAAqB;AACvB;AACF;AACF;AACmC;AAGrC;AACG;AACM;AACL;AAC6B;AACA;AACd;AACA;AACX;AACC;AAGP;AAGN;AAEA;AAA0D;AAEtD;AAAQ;AAAa;AAAsB;AAC7C;AAEE;AAIE;AAEJ;AAEE;AAIS;AAAiC;AAAe;AAMrD;AAER;AAQA;AAAuB;AAEnB;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AAAM;AACiC;AACrC;AAEF;AACA;AAAmB;AACQ;AACE;AAE7B;AACA;AACA;AACA;AACE;AAA4C;AAE9C;AAA8C;AACO;AACxC;AAGb;AACA;AACA;AAAM;AACJ;AACA;AACA;AACA;AAEF;AACA;AAA0B;AACa;AAClC;AAEL;AAA8B;AAEoC;AAC7D;AAEL;AAAsB;AAElB;AACG;AAAsB;AAAkB;AAAgB;AAAO;AAEpE;AACc;AAGhB;AAAqB;AAEjB;AAEA;AAAkD;AACpD;AACiB;AAGnB;AAAsB;AAElB;AACE;AAAA;AAGF;AACA;AACA;AACA;AAAmC;AACrC;AACwD;AAG1D;AAAsB;AAElB;AAEA;AACE;AAAA;AAIF;AACE;AAA6B;AAI/B;AACE;AAA2B;AAG7B;AAEE;AACE;AACA;AAAsC;AAIxC;AACE;AACA;AAA0C;AAI5C;AACE;AAEA;AACA;AAAoB;AAItB;AACE;AACA;AACA;AAAmC;AACrC;AAGA;AACE;AACA;AAAuB;AAIzB;AACE;AACA;AAAO;AAIT;AACE;AACA;AAAmB;AAIrB;AACE;AACA;AAAyB;AAI3B;AACE;AACA;AAA2B;AAI7B;AACE;AACA;AAAkC;AAIpC;AACE;AACA;AAAyB;AAC3B;AACF;AACF;AACA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAGF;AAAoB;AAEhB;AAEA;AACE;AAAe;AACjB;AACF;AACoB;AAGtB;AAAmB;AAEf;AAEA;AACE;AAAgB;AAClB;AACF;AACmB;AAGrB;AAAwC;AACX;AACwB;AAErD;AAA2C;AAEvC;AAEA;AACE;AAAuC;AACzC;AACF;AACsD;AAGxD;AAA6C;AAGrC;AACQ;AACe;AACJ;AACA;AACQ;AACvB;AACF;AAED;AACP;AACE;AACA;AACA;AACA;AACF;AAGF;AAAA;AACE;AAEE;AAA2C;AAC7C;AACO;AAIT;AACE;AACE;AAAM;AACR;AAKF;AACE;AACE;AAAO;AACT;AAGF;AACG;AACC;AACc;AACJ;AAET;AACC;AACuC;AACxB;AACJ;AACgB;AACE;AACzB;AACA;AACM;AACA;AACC;AACF;AACD;AACR;AACY;AACO;AAEpB;AACC;AACA;AACgB;AACG;AACV;AACL;AACI;AACM;AACd;AAEJ;AAGN;AAWA;AAAqB;AAKjB;AACA;AACA;AACA;AACA;AACA;AAEA;AAAiB;AAEb;AAAqC;AACvC;AACO;AAGT;AACE;AACE;AAAyB;AAC3B;AAGF;AACE;AAA+B;AACzB;AACkC;AACH;AACnC;AACD;AAGH;AACE;AAA+B;AACK;AACD;AAClC;AAGH;AAAc;AAEV;AACE;AAAgB;AACd;AAGW;AAEb;AAAwB;AAC1B;AACF;AACO;AAGT;AACE;AAAuB;AAGzB;AACE;AACA;AAAK;AAGP;AACE;AACA;AAA6B;AAG/B;AAAmB;AAEf;AACA;AAA4B;AAC9B;AACc;AAGhB;AAAqB;AAEjB;AAEA;AACE;AAAA;AAGF;AAAa;AACJ;AAET;AAEA;AAEA;AAEA;AACE;AAAwB;AAExB;AAAY;AACd;AACF;AACyD;AAG3D;AACG;AACQ;AACL;AACA;AACA;AACF;AAEC;AACQ;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;AAEC;AAAc;AAAiB;AAAmB;AAIvD;AAGN;AAQA;AAAuB;AAEnB;AACA;AACA;AACA;AACA;AAAmB;AACmB;AACA;AAGtC;AACG;AACM;AACD;AACC;AACK;AAGZ;AAGN;AAEA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;;"}