@liveblocks/react-ui 2.21.0-emails3 → 2.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/dist/components/Comment.cjs.map +1 -1
  2. package/dist/components/Comment.js.map +1 -1
  3. package/dist/components/Composer.cjs.map +1 -1
  4. package/dist/components/Composer.js.map +1 -1
  5. package/dist/components/HistoryVersionSummary.cjs.map +1 -1
  6. package/dist/components/HistoryVersionSummary.js.map +1 -1
  7. package/dist/components/HistoryVersionSummaryList.cjs.map +1 -1
  8. package/dist/components/HistoryVersionSummaryList.js.map +1 -1
  9. package/dist/components/InboxNotification.cjs.map +1 -1
  10. package/dist/components/InboxNotification.js.map +1 -1
  11. package/dist/components/InboxNotificationList.cjs.map +1 -1
  12. package/dist/components/InboxNotificationList.js.map +1 -1
  13. package/dist/components/Thread.cjs.map +1 -1
  14. package/dist/components/Thread.js.map +1 -1
  15. package/dist/components/internal/Attachment.cjs.map +1 -1
  16. package/dist/components/internal/Attachment.js.map +1 -1
  17. package/dist/components/internal/Avatar.cjs.map +1 -1
  18. package/dist/components/internal/Avatar.js.map +1 -1
  19. package/dist/components/internal/Button.cjs.map +1 -1
  20. package/dist/components/internal/Button.js.map +1 -1
  21. package/dist/components/internal/Dropdown.cjs.map +1 -1
  22. package/dist/components/internal/Dropdown.js.map +1 -1
  23. package/dist/components/internal/EmojiPicker.cjs +68 -82
  24. package/dist/components/internal/EmojiPicker.cjs.map +1 -1
  25. package/dist/components/internal/EmojiPicker.js +70 -84
  26. package/dist/components/internal/EmojiPicker.js.map +1 -1
  27. package/dist/components/internal/Room.cjs.map +1 -1
  28. package/dist/components/internal/Room.js.map +1 -1
  29. package/dist/components/internal/Tooltip.cjs.map +1 -1
  30. package/dist/components/internal/Tooltip.js.map +1 -1
  31. package/dist/components/internal/User.cjs.map +1 -1
  32. package/dist/components/internal/User.js.map +1 -1
  33. package/dist/components.cjs.map +1 -1
  34. package/dist/components.js.map +1 -1
  35. package/dist/config.cjs.map +1 -1
  36. package/dist/config.js.map +1 -1
  37. package/dist/index.d.cts +1 -0
  38. package/dist/index.d.ts +1 -0
  39. package/dist/overrides.cjs +1 -0
  40. package/dist/overrides.cjs.map +1 -1
  41. package/dist/overrides.js +1 -0
  42. package/dist/overrides.js.map +1 -1
  43. package/dist/primitives/Composer/index.cjs.map +1 -1
  44. package/dist/primitives/Composer/index.js.map +1 -1
  45. package/dist/primitives/FileSize.cjs.map +1 -1
  46. package/dist/primitives/FileSize.js.map +1 -1
  47. package/dist/primitives/Timestamp.cjs.map +1 -1
  48. package/dist/primitives/Timestamp.js.map +1 -1
  49. package/dist/primitives/index.cjs +0 -2
  50. package/dist/primitives/index.cjs.map +1 -1
  51. package/dist/primitives/index.d.cts +4 -165
  52. package/dist/primitives/index.d.ts +4 -165
  53. package/dist/primitives/index.js +0 -2
  54. package/dist/primitives/index.js.map +1 -1
  55. package/dist/slate/plugins/paste.cjs +8 -1
  56. package/dist/slate/plugins/paste.cjs.map +1 -1
  57. package/dist/slate/plugins/paste.js +8 -1
  58. package/dist/slate/plugins/paste.js.map +1 -1
  59. package/dist/utils/Persist.cjs.map +1 -1
  60. package/dist/utils/Persist.js.map +1 -1
  61. package/dist/utils/Portal.cjs.map +1 -1
  62. package/dist/utils/Portal.js.map +1 -1
  63. package/dist/version.cjs +1 -1
  64. package/dist/version.cjs.map +1 -1
  65. package/dist/version.js +1 -1
  66. package/dist/version.js.map +1 -1
  67. package/package.json +5 -7
  68. package/src/styles/index.css +54 -10
  69. package/styles.css +1 -1
  70. package/styles.css.map +1 -1
  71. package/dist/primitives/EmojiPicker/contexts.cjs +0 -19
  72. package/dist/primitives/EmojiPicker/contexts.cjs.map +0 -1
  73. package/dist/primitives/EmojiPicker/contexts.js +0 -16
  74. package/dist/primitives/EmojiPicker/contexts.js.map +0 -1
  75. package/dist/primitives/EmojiPicker/index.cjs +0 -516
  76. package/dist/primitives/EmojiPicker/index.cjs.map +0 -1
  77. package/dist/primitives/EmojiPicker/index.js +0 -512
  78. package/dist/primitives/EmojiPicker/index.js.map +0 -1
  79. package/dist/primitives/EmojiPicker/utils.cjs +0 -330
  80. package/dist/primitives/EmojiPicker/utils.cjs.map +0 -1
  81. package/dist/primitives/EmojiPicker/utils.js +0 -326
  82. package/dist/primitives/EmojiPicker/utils.js.map +0 -1
  83. package/dist/utils/request-idle-callback.cjs +0 -15
  84. package/dist/utils/request-idle-callback.cjs.map +0 -1
  85. package/dist/utils/request-idle-callback.js +0 -12
  86. package/dist/utils/request-idle-callback.js.map +0 -1
  87. package/dist/utils/visually-hidden.cjs +0 -17
  88. package/dist/utils/visually-hidden.cjs.map +0 -1
  89. package/dist/utils/visually-hidden.js +0 -15
  90. package/dist/utils/visually-hidden.js.map +0 -1
@@ -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 { useRoom } from \"@liveblocks/react\";\nimport {\n useClientOrNull,\n useLayoutEffect,\n useMentionSuggestions,\n useResolveMentionSuggestions,\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 update,\n elements,\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 // Manually update the placement when the number of suggestions changes\n // This can prevent the list of suggestions from scrolling instead of moving to the other placement\n useLayoutEffect(() => {\n if (!isOpen) return;\n\n const mentionSuggestions = elements.floating?.firstChild as\n | HTMLElement\n | undefined;\n\n if (!mentionSuggestions) {\n return;\n }\n\n // Force the mention suggestions to grow instead of scrolling\n mentionSuggestions.style.overflowY = \"visible\";\n mentionSuggestions.style.maxHeight = \"none\";\n\n // Trigger a placement update\n update();\n\n // Reset the mention suggestions after the placement update\n const animationFrame = requestAnimationFrame(() => {\n mentionSuggestions.style.overflowY = \"auto\";\n mentionSuggestions.style.maxHeight =\n \"var(--lb-composer-floating-available-height)\";\n });\n\n return () => {\n cancelAnimationFrame(animationFrame);\n };\n }, [userIds?.length, isOpen, elements.floating, update]);\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 = `liveblocks-floating-toolbar-${id}`;\n const suggestionsListId = `liveblocks-suggestions-list-${id}`;\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 = useRoom({ allowOutsideRoom: true });\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 try {\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 } catch {\n // Slate's DOM-specific methods will throw if the editor's DOM\n // node no longer exists. This action doesn't make sense on an\n // unmounted editor so we can safely ignore it.\n }\n },\n [editor]\n );\n\n const blur = useCallback(() => {\n try {\n ReactEditor.blur(editor);\n } catch {\n // Slate's DOM-specific methods will throw if the editor's DOM\n // node no longer exists. This action doesn't make sense on an\n // unmounted editor so we can safely ignore it.\n }\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 {\n children,\n mark,\n onValueChange,\n onClick,\n onPointerDown,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"button\";\n const { marks, toggleMark } = useComposer();\n\n const handlePointerDown = useCallback(\n (event: PointerEvent<HTMLButtonElement>) => {\n onPointerDown?.(event);\n\n event.preventDefault();\n event.stopPropagation();\n },\n [onPointerDown]\n );\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n event.preventDefault();\n event.stopPropagation();\n\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 onPointerDown={handlePointerDown}\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;AACA;AACA;AACyB;AACzB;AACA;AACW;AACL;AAGR;AACE;AAAkD;AAGpD;AACE;AACE;AAEA;AAAA;AAGF;AACA;AAA6B;AAK/B;AACE;AAAa;AAEb;AAIA;AACE;AAAA;AAIF;AACA;AAGA;AAGA;AACE;AACA;AACE;AAGJ;AACE;AAAmC;AACrC;AAGF;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;AACA;AACA;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;AACE;AAAgB;AACd;AAGW;AAEb;AAAwB;AAC1B;AACA;AAIF;AACF;AACO;AAGT;AACE;AACE;AAAuB;AACvB;AAIF;AAGF;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;AAKvB;AACE;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AAEA;AAA0B;AAEtB;AAEA;AACA;AAAsB;AACxB;AACc;AAGhB;AAAoB;AAEhB;AAEA;AACE;AACA;AAEA;AACA;AAAoB;AACtB;AACF;AACyC;AAG3C;AACG;AACQ;AACQ;AACN;AACM;AACX;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 { useRoom } from \"@liveblocks/react\";\nimport {\n useClientOrNull,\n useLayoutEffect,\n useMentionSuggestions,\n useResolveMentionSuggestions,\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 update,\n elements,\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 // Manually update the placement when the number of suggestions changes\n // This can prevent the list of suggestions from scrolling instead of moving to the other placement\n useLayoutEffect(() => {\n if (!isOpen) return;\n\n const mentionSuggestions = elements.floating?.firstChild as\n | HTMLElement\n | undefined;\n\n if (!mentionSuggestions) {\n return;\n }\n\n // Force the mention suggestions to grow instead of scrolling\n mentionSuggestions.style.overflowY = \"visible\";\n mentionSuggestions.style.maxHeight = \"none\";\n\n // Trigger a placement update\n update();\n\n // Reset the mention suggestions after the placement update\n const animationFrame = requestAnimationFrame(() => {\n mentionSuggestions.style.overflowY = \"auto\";\n mentionSuggestions.style.maxHeight =\n \"var(--lb-composer-floating-available-height)\";\n });\n\n return () => {\n cancelAnimationFrame(animationFrame);\n };\n }, [userIds?.length, isOpen, elements.floating, update]);\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 = `liveblocks-floating-toolbar-${id}`;\n const suggestionsListId = `liveblocks-suggestions-list-${id}`;\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 = useRoom({ allowOutsideRoom: true });\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 try {\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 } catch {\n // Slate's DOM-specific methods will throw if the editor's DOM\n // node no longer exists. This action doesn't make sense on an\n // unmounted editor so we can safely ignore it.\n }\n },\n [editor]\n );\n\n const blur = useCallback(() => {\n try {\n ReactEditor.blur(editor);\n } catch {\n // Slate's DOM-specific methods will throw if the editor's DOM\n // node no longer exists. This action doesn't make sense on an\n // unmounted editor so we can safely ignore it.\n }\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 {\n children,\n mark,\n onValueChange,\n onClick,\n onPointerDown,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"button\";\n const { marks, toggleMark } = useComposer();\n\n const handlePointerDown = useCallback(\n (event: PointerEvent<HTMLButtonElement>) => {\n onPointerDown?.(event);\n\n event.preventDefault();\n event.stopPropagation();\n },\n [onPointerDown]\n );\n\n const handleClick = useCallback(\n (event: MouseEvent<HTMLButtonElement>) => {\n onClick?.(event);\n\n if (!event.isDefaultPrevented()) {\n event.preventDefault();\n event.stopPropagation();\n\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 onPointerDown={handlePointerDown}\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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;AACA;AACA;AACyB;AACzB;AACA;AACW;AACL;AAGR;AACE;AAAkD;AAGpD;AACE;AACE;AAEA;AAAA;AAGF;AACA;AAA6B;AAK/B;AACE;AAAa;AAEb;AAIA;AACE;AAAA;AAIF;AACA;AAGA;AAGA;AACE;AACA;AACE;AAGJ;AACE;AAAmC;AACrC;AAGF;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;AACA;AACA;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;AACE;AAAgB;AACd;AAGW;AAEb;AAAwB;AAC1B;AACA;AAIF;AACF;AACO;AAGT;AACE;AACE;AAAuB;AACvB;AAIF;AAGF;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;AAKvB;AACE;AACA;AACA;AACA;AACA;AACA;AACG;AAIL;AACA;AAEA;AAA0B;AAEtB;AAEA;AACA;AAAsB;AACxB;AACc;AAGhB;AAAoB;AAEhB;AAEA;AACE;AACA;AAEA;AACA;AAAoB;AACtB;AACF;AACyC;AAG3C;AACG;AACQ;AACQ;AACN;AACM;AACX;AAEH;AAAc;AAAY;AACxB;AACH;AACF;AAGN;AAEA;AACE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACF;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"FileSize.cjs","sources":["../../src/primitives/FileSize.tsx"],"sourcesContent":["\"use client\";\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport type { ReactNode } from \"react\";\nimport { forwardRef, useMemo } from \"react\";\n\nimport type { ComponentPropsWithSlot } from \"../types\";\nimport { formatFileSize } from \"../utils/format-file-size\";\n\nconst FILE_SIZE_NAME = \"FileSize\";\n\nexport interface FileSizeProps\n extends Omit<ComponentPropsWithSlot<\"span\">, \"children\"> {\n /**\n * The file size to display.\n */\n size: number;\n\n /**\n * A function to format the displayed file size.\n */\n children?: (size: number, locale?: string) => ReactNode;\n\n /**\n * The locale used when formatting the file size.\n */\n locale?: string;\n}\n\n/**\n * Displays a formatted file size.\n *\n * @example\n * <FileSize size={100000} />\n */\nexport const FileSize = forwardRef<HTMLSpanElement, FileSizeProps>(\n (\n {\n size,\n locale,\n children: renderChildren = formatFileSize,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"span\";\n const children = useMemo(\n () =>\n typeof renderChildren === \"function\"\n ? renderChildren(size, locale)\n : renderChildren,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [renderChildren, size]\n );\n\n return (\n <Component {...props} ref={forwardedRef}>\n {children}\n </Component>\n );\n }\n);\n\nif (process.env.NODE_ENV !== \"production\") {\n FileSize.displayName = FILE_SIZE_NAME;\n}\n"],"names":[],"mappings":";;;;;;;;AAAA;AASA;AA0BO;AAAiB;AAEpB;AACE;AACA;AAC2B;AAC3B;AACG;AAIL;AACA;AAAiB;AAIT;AAEe;AAGvB;AACG;AAAc;AAAY;AACxB;AACH;AAGN;AAEA;AACE;AACF;;"}
1
+ {"version":3,"file":"FileSize.cjs","sources":["../../src/primitives/FileSize.tsx"],"sourcesContent":["\"use client\";\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport type { ReactNode } from \"react\";\nimport { forwardRef, useMemo } from \"react\";\n\nimport type { ComponentPropsWithSlot } from \"../types\";\nimport { formatFileSize } from \"../utils/format-file-size\";\n\nconst FILE_SIZE_NAME = \"FileSize\";\n\nexport interface FileSizeProps\n extends Omit<ComponentPropsWithSlot<\"span\">, \"children\"> {\n /**\n * The file size to display.\n */\n size: number;\n\n /**\n * A function to format the displayed file size.\n */\n children?: (size: number, locale?: string) => ReactNode;\n\n /**\n * The locale used when formatting the file size.\n */\n locale?: string;\n}\n\n/**\n * Displays a formatted file size.\n *\n * @example\n * <FileSize size={100000} />\n */\nexport const FileSize = forwardRef<HTMLSpanElement, FileSizeProps>(\n (\n {\n size,\n locale,\n children: renderChildren = formatFileSize,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"span\";\n const children = useMemo(\n () =>\n typeof renderChildren === \"function\"\n ? renderChildren(size, locale)\n : renderChildren,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [renderChildren, size]\n );\n\n return (\n <Component {...props} ref={forwardedRef}>\n {children}\n </Component>\n );\n }\n);\n\nif (process.env.NODE_ENV !== \"production\") {\n FileSize.displayName = FILE_SIZE_NAME;\n}\n"],"names":[],"mappings":";;;;;;;;;AASA;AA0BO;AAAiB;AAEpB;AACE;AACA;AAC2B;AAC3B;AACG;AAIL;AACA;AAAiB;AAIT;AAEe;AAGvB;AACG;AAAc;AAAY;AACxB;AACH;AAGN;AAEA;AACE;AACF;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"FileSize.js","sources":["../../src/primitives/FileSize.tsx"],"sourcesContent":["\"use client\";\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport type { ReactNode } from \"react\";\nimport { forwardRef, useMemo } from \"react\";\n\nimport type { ComponentPropsWithSlot } from \"../types\";\nimport { formatFileSize } from \"../utils/format-file-size\";\n\nconst FILE_SIZE_NAME = \"FileSize\";\n\nexport interface FileSizeProps\n extends Omit<ComponentPropsWithSlot<\"span\">, \"children\"> {\n /**\n * The file size to display.\n */\n size: number;\n\n /**\n * A function to format the displayed file size.\n */\n children?: (size: number, locale?: string) => ReactNode;\n\n /**\n * The locale used when formatting the file size.\n */\n locale?: string;\n}\n\n/**\n * Displays a formatted file size.\n *\n * @example\n * <FileSize size={100000} />\n */\nexport const FileSize = forwardRef<HTMLSpanElement, FileSizeProps>(\n (\n {\n size,\n locale,\n children: renderChildren = formatFileSize,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"span\";\n const children = useMemo(\n () =>\n typeof renderChildren === \"function\"\n ? renderChildren(size, locale)\n : renderChildren,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [renderChildren, size]\n );\n\n return (\n <Component {...props} ref={forwardedRef}>\n {children}\n </Component>\n );\n }\n);\n\nif (process.env.NODE_ENV !== \"production\") {\n FileSize.displayName = FILE_SIZE_NAME;\n}\n"],"names":[],"mappings":";;;;;;AAAA;AASA;AA0BO;AAAiB;AAEpB;AACE;AACA;AAC2B;AAC3B;AACG;AAIL;AACA;AAAiB;AAIT;AAEe;AAGvB;AACG;AAAc;AAAY;AACxB;AACH;AAGN;AAEA;AACE;AACF;;"}
1
+ {"version":3,"file":"FileSize.js","sources":["../../src/primitives/FileSize.tsx"],"sourcesContent":["\"use client\";\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport type { ReactNode } from \"react\";\nimport { forwardRef, useMemo } from \"react\";\n\nimport type { ComponentPropsWithSlot } from \"../types\";\nimport { formatFileSize } from \"../utils/format-file-size\";\n\nconst FILE_SIZE_NAME = \"FileSize\";\n\nexport interface FileSizeProps\n extends Omit<ComponentPropsWithSlot<\"span\">, \"children\"> {\n /**\n * The file size to display.\n */\n size: number;\n\n /**\n * A function to format the displayed file size.\n */\n children?: (size: number, locale?: string) => ReactNode;\n\n /**\n * The locale used when formatting the file size.\n */\n locale?: string;\n}\n\n/**\n * Displays a formatted file size.\n *\n * @example\n * <FileSize size={100000} />\n */\nexport const FileSize = forwardRef<HTMLSpanElement, FileSizeProps>(\n (\n {\n size,\n locale,\n children: renderChildren = formatFileSize,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"span\";\n const children = useMemo(\n () =>\n typeof renderChildren === \"function\"\n ? renderChildren(size, locale)\n : renderChildren,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [renderChildren, size]\n );\n\n return (\n <Component {...props} ref={forwardedRef}>\n {children}\n </Component>\n );\n }\n);\n\nif (process.env.NODE_ENV !== \"production\") {\n FileSize.displayName = FILE_SIZE_NAME;\n}\n"],"names":[],"mappings":";;;;;;;AASA;AA0BO;AAAiB;AAEpB;AACE;AACA;AAC2B;AAC3B;AACG;AAIL;AACA;AAAiB;AAIT;AAEe;AAGvB;AACG;AAAc;AAAY;AACxB;AACH;AAGN;AAEA;AACE;AACF;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"Timestamp.cjs","sources":["../../src/primitives/Timestamp.tsx"],"sourcesContent":["\"use client\";\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport type { ReactNode } from \"react\";\nimport { forwardRef, useMemo } from \"react\";\n\nimport type { ComponentPropsWithSlot } from \"../types\";\nimport { capitalize } from \"../utils/capitalize\";\nimport { dateTimeFormat, relativeTimeFormat } from \"../utils/intl\";\nimport { useInterval } from \"../utils/use-interval\";\nimport { useRerender } from \"../utils/use-rerender\";\n\nconst DYNAMIC_DATE_THRESHOLD = 3 * 24 * 60 * 60 * 1000; // 3 days\nconst RENDER_INTERVAL = 30 * 1000; // 30 seconds\n\nconst TIMESTAMP_NAME = \"Timestamp\";\n\nexport interface TimestampProps\n extends Omit<ComponentPropsWithSlot<\"time\">, \"children\" | \"title\"> {\n /**\n * The date to display.\n */\n date: Date | string | number;\n\n /**\n * A function to format the displayed date.\n */\n children?: (date: Date, locale?: string) => ReactNode;\n\n /**\n * The `title` attribute's value or a function to format it.\n */\n title?: string | ((date: Date, locale?: string) => string);\n\n /**\n * The interval in milliseconds at which the component will re-render.\n * Can be set to `false` to disable re-rendering.\n */\n interval?: number | false;\n\n /**\n * The locale used when formatting the date.\n */\n locale?: string;\n}\n\nconst relativeUnits = {\n seconds: 60,\n minutes: 60,\n hours: 24,\n days: 7,\n weeks: 4.34524,\n months: 12,\n};\n\n/**\n * Formats a date absolutely.\n */\nfunction formatVerboseDate(date: Date, locale?: string) {\n const formatter = dateTimeFormat(locale, {\n year: \"numeric\",\n month: \"numeric\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"numeric\",\n });\n\n return capitalize(formatter.format(date));\n}\n\n/**\n * Formats a date absolutely with only the day and month.\n */\nfunction formatShortDate(date: Date, locale?: string) {\n const formatter = dateTimeFormat(locale, {\n month: \"short\",\n day: \"numeric\",\n });\n\n return capitalize(formatter.format(date));\n}\n\n// Some locales' relative formatting can be broken (e.g. \"-1h\") when using the narrow style.\nconst localesWithBrokenNarrowRelativeFormatting = [\n \"br\",\n \"fr\",\n \"nb\",\n \"nn\",\n \"no\",\n \"ro\",\n \"sv\",\n];\n\n/**\n * Formats a date relatively.\n */\nfunction formatRelativeDate(date: Date, locale?: string) {\n let resolvedLocale: string;\n\n if (locale) {\n resolvedLocale = locale;\n } else {\n const formatter = relativeTimeFormat();\n\n resolvedLocale = formatter.resolvedOptions().locale;\n }\n\n const isBrokenWhenNarrow = localesWithBrokenNarrowRelativeFormatting.some(\n (locale) =>\n resolvedLocale === locale || resolvedLocale.startsWith(`${locale}-`)\n );\n\n const formatter = relativeTimeFormat(resolvedLocale, {\n style: isBrokenWhenNarrow ? \"short\" : \"narrow\",\n numeric: \"auto\",\n });\n\n let difference = (date.getTime() - Date.now()) / 1000;\n\n if (\n difference > -relativeUnits.seconds &&\n difference < relativeUnits.seconds\n ) {\n return formatter.format(0, \"seconds\");\n }\n\n for (const [unit, length] of Object.entries(relativeUnits)) {\n if (Math.abs(difference) < length) {\n return formatter.format(\n Math.round(difference),\n unit as Intl.RelativeTimeFormatUnit\n );\n }\n\n difference /= length;\n }\n\n return capitalize(formatter.format(Math.round(difference), \"years\"));\n}\n\n/**\n * Formats a date relatively if it's recent,\n * otherwise absolutely with only the day and month.\n */\nfunction formatDynamicDate(date: Date, locale?: string) {\n return date.getTime() > Date.now() - DYNAMIC_DATE_THRESHOLD\n ? formatRelativeDate(date, locale)\n : formatShortDate(date, locale);\n}\n\n/**\n * Displays a formatted date, and automatically re-renders to support relative\n * formatting. Defaults to relative formatting for recent dates and a short\n * absolute formatting for older ones.\n *\n * @example\n * <Timestamp date={new Date()} />\n *\n * @example\n * <Timestamp date={new Date()} title={(date) => date.toISOString()} interval={false}>\n * {(date) => date.toLocaleDateString()}\n * </Timestamp>\n */\nexport const Timestamp = forwardRef<HTMLTimeElement, TimestampProps>(\n (\n {\n date,\n locale,\n children: renderChildren = formatDynamicDate,\n title: renderTitle = formatVerboseDate,\n dateTime,\n interval = RENDER_INTERVAL,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"time\";\n const [rerender, key] = useRerender();\n const parsedDate = useMemo(() => new Date(date), [date]);\n const normalizedDate = useMemo(\n () => parsedDate.toISOString(),\n [parsedDate]\n );\n const title = useMemo(\n () =>\n typeof renderTitle === \"function\"\n ? renderTitle(parsedDate, locale)\n : renderTitle,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [renderTitle, parsedDate, key]\n );\n const children = useMemo(\n () =>\n typeof renderChildren === \"function\"\n ? renderChildren(parsedDate, locale)\n : renderChildren,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [renderChildren, parsedDate, key]\n );\n\n useInterval(rerender, interval);\n\n return (\n <Component\n {...props}\n ref={forwardedRef}\n dateTime={dateTime ?? normalizedDate}\n title={title}\n >\n {children}\n </Component>\n );\n }\n);\n\nif (process.env.NODE_ENV !== \"production\") {\n Timestamp.displayName = TIMESTAMP_NAME;\n}\n"],"names":[],"mappings":";;;;;;;;;;;AAAA;AAYA;AACA;AAEA;AA+BA;AAAsB;AACX;AACA;AACF;AACD;AACC;AAET;AAKA;AACE;AAAyC;AACjC;AACC;AACF;AACC;AACE;AAGV;AACF;AAKA;AACE;AAAyC;AAChC;AACF;AAGP;AACF;AAGA;AAAkD;AAChD;AACA;AACA;AACA;AACA;AACA;AAEF;AAKA;AACE;AAEA;AACE;AAAiB;AAEjB;AAEA;AAA6C;AAG/C;AAAqE;AAEE;AAGvE;AAAqD;AACb;AAC7B;AAGX;AAEA;AAIE;AAAoC;AAGtC;AACE;AACE;AAAiB;AACM;AACrB;AACF;AAGF;AAAc;AAGhB;AACF;AAMA;AACE;AAGF;AAeO;AAAkB;AAErB;AACE;AACA;AAC2B;AACN;AACrB;AACW;AACX;AACG;AAIL;AACA;AACA;AACA;AAAuB;AACQ;AAClB;AAEb;AAAc;AAIN;AAEuB;AAE/B;AAAiB;AAIT;AAE0B;AAGlC;AAEA;AACG;AACK;AACC;AACiB;AACtB;AAEC;AACH;AAGN;AAEA;AACE;AACF;;"}
1
+ {"version":3,"file":"Timestamp.cjs","sources":["../../src/primitives/Timestamp.tsx"],"sourcesContent":["\"use client\";\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport type { ReactNode } from \"react\";\nimport { forwardRef, useMemo } from \"react\";\n\nimport type { ComponentPropsWithSlot } from \"../types\";\nimport { capitalize } from \"../utils/capitalize\";\nimport { dateTimeFormat, relativeTimeFormat } from \"../utils/intl\";\nimport { useInterval } from \"../utils/use-interval\";\nimport { useRerender } from \"../utils/use-rerender\";\n\nconst DYNAMIC_DATE_THRESHOLD = 3 * 24 * 60 * 60 * 1000; // 3 days\nconst RENDER_INTERVAL = 30 * 1000; // 30 seconds\n\nconst TIMESTAMP_NAME = \"Timestamp\";\n\nexport interface TimestampProps\n extends Omit<ComponentPropsWithSlot<\"time\">, \"children\" | \"title\"> {\n /**\n * The date to display.\n */\n date: Date | string | number;\n\n /**\n * A function to format the displayed date.\n */\n children?: (date: Date, locale?: string) => ReactNode;\n\n /**\n * The `title` attribute's value or a function to format it.\n */\n title?: string | ((date: Date, locale?: string) => string);\n\n /**\n * The interval in milliseconds at which the component will re-render.\n * Can be set to `false` to disable re-rendering.\n */\n interval?: number | false;\n\n /**\n * The locale used when formatting the date.\n */\n locale?: string;\n}\n\nconst relativeUnits = {\n seconds: 60,\n minutes: 60,\n hours: 24,\n days: 7,\n weeks: 4.34524,\n months: 12,\n};\n\n/**\n * Formats a date absolutely.\n */\nfunction formatVerboseDate(date: Date, locale?: string) {\n const formatter = dateTimeFormat(locale, {\n year: \"numeric\",\n month: \"numeric\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"numeric\",\n });\n\n return capitalize(formatter.format(date));\n}\n\n/**\n * Formats a date absolutely with only the day and month.\n */\nfunction formatShortDate(date: Date, locale?: string) {\n const formatter = dateTimeFormat(locale, {\n month: \"short\",\n day: \"numeric\",\n });\n\n return capitalize(formatter.format(date));\n}\n\n// Some locales' relative formatting can be broken (e.g. \"-1h\") when using the narrow style.\nconst localesWithBrokenNarrowRelativeFormatting = [\n \"br\",\n \"fr\",\n \"nb\",\n \"nn\",\n \"no\",\n \"ro\",\n \"sv\",\n];\n\n/**\n * Formats a date relatively.\n */\nfunction formatRelativeDate(date: Date, locale?: string) {\n let resolvedLocale: string;\n\n if (locale) {\n resolvedLocale = locale;\n } else {\n const formatter = relativeTimeFormat();\n\n resolvedLocale = formatter.resolvedOptions().locale;\n }\n\n const isBrokenWhenNarrow = localesWithBrokenNarrowRelativeFormatting.some(\n (locale) =>\n resolvedLocale === locale || resolvedLocale.startsWith(`${locale}-`)\n );\n\n const formatter = relativeTimeFormat(resolvedLocale, {\n style: isBrokenWhenNarrow ? \"short\" : \"narrow\",\n numeric: \"auto\",\n });\n\n let difference = (date.getTime() - Date.now()) / 1000;\n\n if (\n difference > -relativeUnits.seconds &&\n difference < relativeUnits.seconds\n ) {\n return formatter.format(0, \"seconds\");\n }\n\n for (const [unit, length] of Object.entries(relativeUnits)) {\n if (Math.abs(difference) < length) {\n return formatter.format(\n Math.round(difference),\n unit as Intl.RelativeTimeFormatUnit\n );\n }\n\n difference /= length;\n }\n\n return capitalize(formatter.format(Math.round(difference), \"years\"));\n}\n\n/**\n * Formats a date relatively if it's recent,\n * otherwise absolutely with only the day and month.\n */\nfunction formatDynamicDate(date: Date, locale?: string) {\n return date.getTime() > Date.now() - DYNAMIC_DATE_THRESHOLD\n ? formatRelativeDate(date, locale)\n : formatShortDate(date, locale);\n}\n\n/**\n * Displays a formatted date, and automatically re-renders to support relative\n * formatting. Defaults to relative formatting for recent dates and a short\n * absolute formatting for older ones.\n *\n * @example\n * <Timestamp date={new Date()} />\n *\n * @example\n * <Timestamp date={new Date()} title={(date) => date.toISOString()} interval={false}>\n * {(date) => date.toLocaleDateString()}\n * </Timestamp>\n */\nexport const Timestamp = forwardRef<HTMLTimeElement, TimestampProps>(\n (\n {\n date,\n locale,\n children: renderChildren = formatDynamicDate,\n title: renderTitle = formatVerboseDate,\n dateTime,\n interval = RENDER_INTERVAL,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"time\";\n const [rerender, key] = useRerender();\n const parsedDate = useMemo(() => new Date(date), [date]);\n const normalizedDate = useMemo(\n () => parsedDate.toISOString(),\n [parsedDate]\n );\n const title = useMemo(\n () =>\n typeof renderTitle === \"function\"\n ? renderTitle(parsedDate, locale)\n : renderTitle,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [renderTitle, parsedDate, key]\n );\n const children = useMemo(\n () =>\n typeof renderChildren === \"function\"\n ? renderChildren(parsedDate, locale)\n : renderChildren,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [renderChildren, parsedDate, key]\n );\n\n useInterval(rerender, interval);\n\n return (\n <Component\n {...props}\n ref={forwardedRef}\n dateTime={dateTime ?? normalizedDate}\n title={title}\n >\n {children}\n </Component>\n );\n }\n);\n\nif (process.env.NODE_ENV !== \"production\") {\n Timestamp.displayName = TIMESTAMP_NAME;\n}\n"],"names":[],"mappings":";;;;;;;;;;;;AAYA;AACA;AAEA;AA+BA;AAAsB;AACX;AACA;AACF;AACD;AACC;AAET;AAKA;AACE;AAAyC;AACjC;AACC;AACF;AACC;AACE;AAGV;AACF;AAKA;AACE;AAAyC;AAChC;AACF;AAGP;AACF;AAGA;AAAkD;AAChD;AACA;AACA;AACA;AACA;AACA;AAEF;AAKA;AACE;AAEA;AACE;AAAiB;AAEjB;AAEA;AAA6C;AAG/C;AAAqE;AAEE;AAGvE;AAAqD;AACb;AAC7B;AAGX;AAEA;AAIE;AAAoC;AAGtC;AACE;AACE;AAAiB;AACM;AACrB;AACF;AAGF;AAAc;AAGhB;AACF;AAMA;AACE;AAGF;AAeO;AAAkB;AAErB;AACE;AACA;AAC2B;AACN;AACrB;AACW;AACX;AACG;AAIL;AACA;AACA;AACA;AAAuB;AACQ;AAClB;AAEb;AAAc;AAIN;AAEuB;AAE/B;AAAiB;AAIT;AAE0B;AAGlC;AAEA;AACG;AACK;AACC;AACiB;AACtB;AAEC;AACH;AAGN;AAEA;AACE;AACF;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"Timestamp.js","sources":["../../src/primitives/Timestamp.tsx"],"sourcesContent":["\"use client\";\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport type { ReactNode } from \"react\";\nimport { forwardRef, useMemo } from \"react\";\n\nimport type { ComponentPropsWithSlot } from \"../types\";\nimport { capitalize } from \"../utils/capitalize\";\nimport { dateTimeFormat, relativeTimeFormat } from \"../utils/intl\";\nimport { useInterval } from \"../utils/use-interval\";\nimport { useRerender } from \"../utils/use-rerender\";\n\nconst DYNAMIC_DATE_THRESHOLD = 3 * 24 * 60 * 60 * 1000; // 3 days\nconst RENDER_INTERVAL = 30 * 1000; // 30 seconds\n\nconst TIMESTAMP_NAME = \"Timestamp\";\n\nexport interface TimestampProps\n extends Omit<ComponentPropsWithSlot<\"time\">, \"children\" | \"title\"> {\n /**\n * The date to display.\n */\n date: Date | string | number;\n\n /**\n * A function to format the displayed date.\n */\n children?: (date: Date, locale?: string) => ReactNode;\n\n /**\n * The `title` attribute's value or a function to format it.\n */\n title?: string | ((date: Date, locale?: string) => string);\n\n /**\n * The interval in milliseconds at which the component will re-render.\n * Can be set to `false` to disable re-rendering.\n */\n interval?: number | false;\n\n /**\n * The locale used when formatting the date.\n */\n locale?: string;\n}\n\nconst relativeUnits = {\n seconds: 60,\n minutes: 60,\n hours: 24,\n days: 7,\n weeks: 4.34524,\n months: 12,\n};\n\n/**\n * Formats a date absolutely.\n */\nfunction formatVerboseDate(date: Date, locale?: string) {\n const formatter = dateTimeFormat(locale, {\n year: \"numeric\",\n month: \"numeric\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"numeric\",\n });\n\n return capitalize(formatter.format(date));\n}\n\n/**\n * Formats a date absolutely with only the day and month.\n */\nfunction formatShortDate(date: Date, locale?: string) {\n const formatter = dateTimeFormat(locale, {\n month: \"short\",\n day: \"numeric\",\n });\n\n return capitalize(formatter.format(date));\n}\n\n// Some locales' relative formatting can be broken (e.g. \"-1h\") when using the narrow style.\nconst localesWithBrokenNarrowRelativeFormatting = [\n \"br\",\n \"fr\",\n \"nb\",\n \"nn\",\n \"no\",\n \"ro\",\n \"sv\",\n];\n\n/**\n * Formats a date relatively.\n */\nfunction formatRelativeDate(date: Date, locale?: string) {\n let resolvedLocale: string;\n\n if (locale) {\n resolvedLocale = locale;\n } else {\n const formatter = relativeTimeFormat();\n\n resolvedLocale = formatter.resolvedOptions().locale;\n }\n\n const isBrokenWhenNarrow = localesWithBrokenNarrowRelativeFormatting.some(\n (locale) =>\n resolvedLocale === locale || resolvedLocale.startsWith(`${locale}-`)\n );\n\n const formatter = relativeTimeFormat(resolvedLocale, {\n style: isBrokenWhenNarrow ? \"short\" : \"narrow\",\n numeric: \"auto\",\n });\n\n let difference = (date.getTime() - Date.now()) / 1000;\n\n if (\n difference > -relativeUnits.seconds &&\n difference < relativeUnits.seconds\n ) {\n return formatter.format(0, \"seconds\");\n }\n\n for (const [unit, length] of Object.entries(relativeUnits)) {\n if (Math.abs(difference) < length) {\n return formatter.format(\n Math.round(difference),\n unit as Intl.RelativeTimeFormatUnit\n );\n }\n\n difference /= length;\n }\n\n return capitalize(formatter.format(Math.round(difference), \"years\"));\n}\n\n/**\n * Formats a date relatively if it's recent,\n * otherwise absolutely with only the day and month.\n */\nfunction formatDynamicDate(date: Date, locale?: string) {\n return date.getTime() > Date.now() - DYNAMIC_DATE_THRESHOLD\n ? formatRelativeDate(date, locale)\n : formatShortDate(date, locale);\n}\n\n/**\n * Displays a formatted date, and automatically re-renders to support relative\n * formatting. Defaults to relative formatting for recent dates and a short\n * absolute formatting for older ones.\n *\n * @example\n * <Timestamp date={new Date()} />\n *\n * @example\n * <Timestamp date={new Date()} title={(date) => date.toISOString()} interval={false}>\n * {(date) => date.toLocaleDateString()}\n * </Timestamp>\n */\nexport const Timestamp = forwardRef<HTMLTimeElement, TimestampProps>(\n (\n {\n date,\n locale,\n children: renderChildren = formatDynamicDate,\n title: renderTitle = formatVerboseDate,\n dateTime,\n interval = RENDER_INTERVAL,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"time\";\n const [rerender, key] = useRerender();\n const parsedDate = useMemo(() => new Date(date), [date]);\n const normalizedDate = useMemo(\n () => parsedDate.toISOString(),\n [parsedDate]\n );\n const title = useMemo(\n () =>\n typeof renderTitle === \"function\"\n ? renderTitle(parsedDate, locale)\n : renderTitle,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [renderTitle, parsedDate, key]\n );\n const children = useMemo(\n () =>\n typeof renderChildren === \"function\"\n ? renderChildren(parsedDate, locale)\n : renderChildren,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [renderChildren, parsedDate, key]\n );\n\n useInterval(rerender, interval);\n\n return (\n <Component\n {...props}\n ref={forwardedRef}\n dateTime={dateTime ?? normalizedDate}\n title={title}\n >\n {children}\n </Component>\n );\n }\n);\n\nif (process.env.NODE_ENV !== \"production\") {\n Timestamp.displayName = TIMESTAMP_NAME;\n}\n"],"names":[],"mappings":";;;;;;;;;AAAA;AAYA;AACA;AAEA;AA+BA;AAAsB;AACX;AACA;AACF;AACD;AACC;AAET;AAKA;AACE;AAAyC;AACjC;AACC;AACF;AACC;AACE;AAGV;AACF;AAKA;AACE;AAAyC;AAChC;AACF;AAGP;AACF;AAGA;AAAkD;AAChD;AACA;AACA;AACA;AACA;AACA;AAEF;AAKA;AACE;AAEA;AACE;AAAiB;AAEjB;AAEA;AAA6C;AAG/C;AAAqE;AAEE;AAGvE;AAAqD;AACb;AAC7B;AAGX;AAEA;AAIE;AAAoC;AAGtC;AACE;AACE;AAAiB;AACM;AACrB;AACF;AAGF;AAAc;AAGhB;AACF;AAMA;AACE;AAGF;AAeO;AAAkB;AAErB;AACE;AACA;AAC2B;AACN;AACrB;AACW;AACX;AACG;AAIL;AACA;AACA;AACA;AAAuB;AACQ;AAClB;AAEb;AAAc;AAIN;AAEuB;AAE/B;AAAiB;AAIT;AAE0B;AAGlC;AAEA;AACG;AACK;AACC;AACiB;AACtB;AAEC;AACH;AAGN;AAEA;AACE;AACF;;"}
1
+ {"version":3,"file":"Timestamp.js","sources":["../../src/primitives/Timestamp.tsx"],"sourcesContent":["\"use client\";\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport type { ReactNode } from \"react\";\nimport { forwardRef, useMemo } from \"react\";\n\nimport type { ComponentPropsWithSlot } from \"../types\";\nimport { capitalize } from \"../utils/capitalize\";\nimport { dateTimeFormat, relativeTimeFormat } from \"../utils/intl\";\nimport { useInterval } from \"../utils/use-interval\";\nimport { useRerender } from \"../utils/use-rerender\";\n\nconst DYNAMIC_DATE_THRESHOLD = 3 * 24 * 60 * 60 * 1000; // 3 days\nconst RENDER_INTERVAL = 30 * 1000; // 30 seconds\n\nconst TIMESTAMP_NAME = \"Timestamp\";\n\nexport interface TimestampProps\n extends Omit<ComponentPropsWithSlot<\"time\">, \"children\" | \"title\"> {\n /**\n * The date to display.\n */\n date: Date | string | number;\n\n /**\n * A function to format the displayed date.\n */\n children?: (date: Date, locale?: string) => ReactNode;\n\n /**\n * The `title` attribute's value or a function to format it.\n */\n title?: string | ((date: Date, locale?: string) => string);\n\n /**\n * The interval in milliseconds at which the component will re-render.\n * Can be set to `false` to disable re-rendering.\n */\n interval?: number | false;\n\n /**\n * The locale used when formatting the date.\n */\n locale?: string;\n}\n\nconst relativeUnits = {\n seconds: 60,\n minutes: 60,\n hours: 24,\n days: 7,\n weeks: 4.34524,\n months: 12,\n};\n\n/**\n * Formats a date absolutely.\n */\nfunction formatVerboseDate(date: Date, locale?: string) {\n const formatter = dateTimeFormat(locale, {\n year: \"numeric\",\n month: \"numeric\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"numeric\",\n });\n\n return capitalize(formatter.format(date));\n}\n\n/**\n * Formats a date absolutely with only the day and month.\n */\nfunction formatShortDate(date: Date, locale?: string) {\n const formatter = dateTimeFormat(locale, {\n month: \"short\",\n day: \"numeric\",\n });\n\n return capitalize(formatter.format(date));\n}\n\n// Some locales' relative formatting can be broken (e.g. \"-1h\") when using the narrow style.\nconst localesWithBrokenNarrowRelativeFormatting = [\n \"br\",\n \"fr\",\n \"nb\",\n \"nn\",\n \"no\",\n \"ro\",\n \"sv\",\n];\n\n/**\n * Formats a date relatively.\n */\nfunction formatRelativeDate(date: Date, locale?: string) {\n let resolvedLocale: string;\n\n if (locale) {\n resolvedLocale = locale;\n } else {\n const formatter = relativeTimeFormat();\n\n resolvedLocale = formatter.resolvedOptions().locale;\n }\n\n const isBrokenWhenNarrow = localesWithBrokenNarrowRelativeFormatting.some(\n (locale) =>\n resolvedLocale === locale || resolvedLocale.startsWith(`${locale}-`)\n );\n\n const formatter = relativeTimeFormat(resolvedLocale, {\n style: isBrokenWhenNarrow ? \"short\" : \"narrow\",\n numeric: \"auto\",\n });\n\n let difference = (date.getTime() - Date.now()) / 1000;\n\n if (\n difference > -relativeUnits.seconds &&\n difference < relativeUnits.seconds\n ) {\n return formatter.format(0, \"seconds\");\n }\n\n for (const [unit, length] of Object.entries(relativeUnits)) {\n if (Math.abs(difference) < length) {\n return formatter.format(\n Math.round(difference),\n unit as Intl.RelativeTimeFormatUnit\n );\n }\n\n difference /= length;\n }\n\n return capitalize(formatter.format(Math.round(difference), \"years\"));\n}\n\n/**\n * Formats a date relatively if it's recent,\n * otherwise absolutely with only the day and month.\n */\nfunction formatDynamicDate(date: Date, locale?: string) {\n return date.getTime() > Date.now() - DYNAMIC_DATE_THRESHOLD\n ? formatRelativeDate(date, locale)\n : formatShortDate(date, locale);\n}\n\n/**\n * Displays a formatted date, and automatically re-renders to support relative\n * formatting. Defaults to relative formatting for recent dates and a short\n * absolute formatting for older ones.\n *\n * @example\n * <Timestamp date={new Date()} />\n *\n * @example\n * <Timestamp date={new Date()} title={(date) => date.toISOString()} interval={false}>\n * {(date) => date.toLocaleDateString()}\n * </Timestamp>\n */\nexport const Timestamp = forwardRef<HTMLTimeElement, TimestampProps>(\n (\n {\n date,\n locale,\n children: renderChildren = formatDynamicDate,\n title: renderTitle = formatVerboseDate,\n dateTime,\n interval = RENDER_INTERVAL,\n asChild,\n ...props\n },\n forwardedRef\n ) => {\n const Component = asChild ? Slot : \"time\";\n const [rerender, key] = useRerender();\n const parsedDate = useMemo(() => new Date(date), [date]);\n const normalizedDate = useMemo(\n () => parsedDate.toISOString(),\n [parsedDate]\n );\n const title = useMemo(\n () =>\n typeof renderTitle === \"function\"\n ? renderTitle(parsedDate, locale)\n : renderTitle,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [renderTitle, parsedDate, key]\n );\n const children = useMemo(\n () =>\n typeof renderChildren === \"function\"\n ? renderChildren(parsedDate, locale)\n : renderChildren,\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [renderChildren, parsedDate, key]\n );\n\n useInterval(rerender, interval);\n\n return (\n <Component\n {...props}\n ref={forwardedRef}\n dateTime={dateTime ?? normalizedDate}\n title={title}\n >\n {children}\n </Component>\n );\n }\n);\n\nif (process.env.NODE_ENV !== \"production\") {\n Timestamp.displayName = TIMESTAMP_NAME;\n}\n"],"names":[],"mappings":";;;;;;;;;;AAYA;AACA;AAEA;AA+BA;AAAsB;AACX;AACA;AACF;AACD;AACC;AAET;AAKA;AACE;AAAyC;AACjC;AACC;AACF;AACC;AACE;AAGV;AACF;AAKA;AACE;AAAyC;AAChC;AACF;AAGP;AACF;AAGA;AAAkD;AAChD;AACA;AACA;AACA;AACA;AACA;AAEF;AAKA;AACE;AAEA;AACE;AAAiB;AAEjB;AAEA;AAA6C;AAG/C;AAAqE;AAEE;AAGvE;AAAqD;AACb;AAC7B;AAGX;AAEA;AAIE;AAAoC;AAGtC;AACE;AACE;AAAiB;AACM;AACrB;AACF;AAGF;AAAc;AAGhB;AACF;AAMA;AACE;AAGF;AAeO;AAAkB;AAErB;AACE;AACA;AAC2B;AACN;AACrB;AACW;AACX;AACG;AAIL;AACA;AACA;AACA;AAAuB;AACQ;AAClB;AAEb;AAAc;AAIN;AAEuB;AAE/B;AAAiB;AAIT;AAE0B;AAGlC;AAEA;AACG;AACK;AACC;AACiB;AACtB;AAEC;AACH;AAGN;AAEA;AACE;AACF;;"}
@@ -4,7 +4,6 @@ var index = require('./Comment/index.cjs');
4
4
  var index$1 = require('./Composer/index.cjs');
5
5
  var contexts = require('./Composer/contexts.cjs');
6
6
  var utils = require('./Composer/utils.cjs');
7
- var index$2 = require('./EmojiPicker/index.cjs');
8
7
  var FileSize = require('./FileSize.cjs');
9
8
  var Timestamp = require('./Timestamp.cjs');
10
9
 
@@ -14,7 +13,6 @@ exports.Comment = index;
14
13
  exports.Composer = index$1;
15
14
  exports.useComposer = contexts.useComposer;
16
15
  exports.AttachmentTooLargeError = utils.AttachmentTooLargeError;
17
- exports.EmojiPicker = index$2;
18
16
  exports.FileSize = FileSize.FileSize;
19
17
  exports.Timestamp = Timestamp.Timestamp;
20
18
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;"}
@@ -1,7 +1,6 @@
1
1
  import * as react from 'react';
2
- import { ElementType, ComponentPropsWithoutRef, ReactNode, ComponentType, FormEvent, PropsWithChildren } from 'react';
2
+ import { ElementType, ComponentPropsWithoutRef, ReactNode, ComponentType, FormEvent } from 'react';
3
3
  import { CommentBody as CommentBody$1, CommentAttachment, CommentMixedAttachment } from '@liveblocks/core';
4
- import * as react_jsx_runtime from 'react/jsx-runtime';
5
4
 
6
5
  type Direction = "ltr" | "rtl";
7
6
  type SlotProp = {
@@ -89,7 +88,7 @@ declare const CommentLink: react.ForwardRefExoticComponent<Omit<react.DetailedHT
89
88
  */
90
89
  declare const CommentBody: react.ForwardRefExoticComponent<CommentBodyProps & react.RefAttributes<HTMLDivElement>>;
91
90
 
92
- declare namespace index$2 {
91
+ declare namespace index$1 {
93
92
  export {
94
93
  CommentBody as Body,
95
94
  CommentLink as Link,
@@ -343,7 +342,7 @@ declare const ComposerAttachmentsDropArea: react.ForwardRefExoticComponent<Compo
343
342
  */
344
343
  declare const ComposerMarkToggle: react.ForwardRefExoticComponent<ComposerMarkToggleProps & react.RefAttributes<HTMLButtonElement>>;
345
344
 
346
- declare namespace index$1 {
345
+ declare namespace index {
347
346
  export {
348
347
  ComposerAttachFiles as AttachFiles,
349
348
  ComposerAttachmentsDropArea as AttachmentsDropArea,
@@ -435,166 +434,6 @@ declare class AttachmentTooLargeError extends Error {
435
434
  constructor(message: string, origin?: "client" | "server");
436
435
  }
437
436
 
438
- type EmojiPickerContentLoadingProps = ComponentPropsWithoutRef<"div">;
439
- type EmojiPickerContentEmptyProps = ComponentPropsWithoutRef<"div">;
440
- type EmojiPickerContentGridProps = ComponentPropsWithoutRef<"div">;
441
- interface EmojiPickerContentErrorProps extends ComponentPropsWithoutRef<"div"> {
442
- /**
443
- * The error.
444
- */
445
- error: Error;
446
- }
447
- interface EmojiPickerContentCategoryHeaderProps extends ComponentPropsWithoutRef<"div"> {
448
- /**
449
- * The category's name.
450
- */
451
- category: string;
452
- }
453
- type EmojiPickerContentEmojiRowAttributes = {
454
- /**
455
- * The current row's index.
456
- */
457
- rowIndex: number;
458
- /**
459
- * The current row's index within the current category.
460
- */
461
- categoryRowIndex: number;
462
- /**
463
- * The number of rows within the current category.
464
- */
465
- categoryRowsCount: number;
466
- };
467
- interface EmojiPickerContentRowProps extends ComponentPropsWithoutRef<"div"> {
468
- /**
469
- * Attributes related to the current row.
470
- */
471
- attributes: EmojiPickerContentEmojiRowAttributes;
472
- }
473
- interface EmojiPickerContentEmojiProps extends ComponentPropsWithoutRef<"button"> {
474
- /**
475
- * The emoji to be displayed.
476
- */
477
- emoji: string;
478
- }
479
- interface EmojiPickerRootProps extends PropsWithChildren {
480
- /**
481
- * The number of emojis per row.
482
- */
483
- columns?: number;
484
- /**
485
- * The event handler called when an emoji is selected.
486
- */
487
- onEmojiSelect?: (emoji: string) => void;
488
- /**
489
- * The locale used when getting emoji data.
490
- */
491
- locale?: string;
492
- }
493
- interface EmojiPickerContentComponents {
494
- /**
495
- * The component displayed when the emoji data is loading.
496
- */
497
- Loading: ComponentType<EmojiPickerContentLoadingProps>;
498
- /**
499
- * The component displayed when there are no results.
500
- */
501
- Empty: ComponentType<EmojiPickerContentEmptyProps>;
502
- /**
503
- * The component displayed when there was an error while getting the emoji data.
504
- */
505
- Error: ComponentType<EmojiPickerContentErrorProps>;
506
- /**
507
- * The component used to display category headers.
508
- */
509
- CategoryHeader: ComponentType<EmojiPickerContentCategoryHeaderProps>;
510
- /**
511
- * The component displayed when there are emojis to display.
512
- */
513
- Grid: ComponentType<EmojiPickerContentGridProps>;
514
- /**
515
- * The component used to display rows of emojis.
516
- */
517
- Row: ComponentType<EmojiPickerContentRowProps>;
518
- /**
519
- * The component used to display emojis.
520
- */
521
- Emoji: ComponentType<EmojiPickerContentEmojiProps>;
522
- }
523
- interface EmojiPickerContentProps extends ComponentPropsWithSlot<"div"> {
524
- /**
525
- * The components displayed within the content.
526
- */
527
- components?: Partial<EmojiPickerContentComponents>;
528
- }
529
- type EmojiPickerSearchProps = ComponentPropsWithSlot<"input">;
530
-
531
- /**
532
- * @private
533
- * The EmojiPicker primitive is undocumented for now and subject to change,
534
- * use at your own risk. If you have any feedback on it, please let us know!
535
- * See how we use it in the default components to learn how to use it:
536
- * https://github.com/liveblocks/liveblocks/blob/main/packages/liveblocks-react-ui/src/components/internal/EmojiPicker.tsx.
537
- *
538
- * Surrounds the emoji picker, it handles emoji data and coordinates
539
- * `EmojiPicker.Search` and `EmojiPicker.Content`.
540
- *
541
- * @example
542
- * <EmojiPicker.Root>
543
- * <EmojiPicker.Search />
544
- * <EmojiPicker.Content />
545
- * </EmojiPicker.Root>
546
- */
547
- declare function EmojiPickerRoot({ columns, locale, onEmojiSelect, children, }: EmojiPickerRootProps): react_jsx_runtime.JSX.Element;
548
- declare namespace EmojiPickerRoot {
549
- var displayName: string;
550
- }
551
- /**
552
- * @private
553
- * The EmojiPicker primitive is undocumented for now and subject to change,
554
- * use at your own risk. If you have any feedback on it, please let us know!
555
- * See how we use it in the default components to learn how to use it:
556
- * https://github.com/liveblocks/liveblocks/blob/main/packages/liveblocks-react-ui/src/components/internal/EmojiPicker.tsx.
557
- *
558
- * The search input of the emoji picker. It also affects the focus and selection
559
- * within `EmojiPicker.Content`.
560
- *
561
- * @example
562
- * <EmojiPicker.Search />
563
- */
564
- declare const EmojiPickerSearch: react.ForwardRefExoticComponent<Omit<react.DetailedHTMLProps<react.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref"> & SlotProp & react.RefAttributes<HTMLInputElement>>;
565
- /**
566
- * @private
567
- * The EmojiPicker primitive is undocumented for now and subject to change,
568
- * use at your own risk. If you have any feedback on it, please let us know!
569
- * See how we use it in the default components to learn how to use it:
570
- * https://github.com/liveblocks/liveblocks/blob/main/packages/liveblocks-react-ui/src/components/internal/EmojiPicker.tsx.
571
- *
572
- * The main content of the emoji picker, either displaying the emoji grid or various
573
- * alternative states (loading, empty, and error).
574
- *
575
- * @example
576
- * <EmojiPicker.Content
577
- * components={{
578
- * Loading: EmojiPickerLoading,
579
- * Empty: EmojiPickerEmpty,
580
- * Error: EmojiPickerError,
581
- * CategoryHeader: EmojiPickerCategoryHeader,
582
- * Grid: EmojiPickerGrid,
583
- * Row: EmojiPickerRow,
584
- * Emoji: EmojiPickerEmoji,
585
- * }}
586
- * />
587
- */
588
- declare const EmojiPickerContent: react.ForwardRefExoticComponent<EmojiPickerContentProps & react.RefAttributes<HTMLDivElement>>;
589
-
590
- declare namespace index {
591
- export {
592
- EmojiPickerContent as Content,
593
- EmojiPickerRoot as Root,
594
- EmojiPickerSearch as Search,
595
- };
596
- }
597
-
598
437
  interface FileSizeProps extends Omit<ComponentPropsWithSlot<"span">, "children"> {
599
438
  /**
600
439
  * The file size to display.
@@ -655,4 +494,4 @@ interface TimestampProps extends Omit<ComponentPropsWithSlot<"time">, "children"
655
494
  */
656
495
  declare const Timestamp: react.ForwardRefExoticComponent<TimestampProps & react.RefAttributes<HTMLTimeElement>>;
657
496
 
658
- export { AttachmentTooLargeError, index$2 as Comment, CommentBodyComponents, CommentBodyLinkProps, CommentBodyMentionProps, CommentBodyProps, CommentLinkProps, CommentMentionProps, index$1 as Composer, ComposerAttachFilesProps, ComposerAttachmentsDropAreaProps, ComposerBodyMark, ComposerBodyMarks, ComposerContext, ComposerEditorComponents, ComposerEditorFloatingToolbarProps, ComposerEditorLinkProps, ComposerEditorMentionProps, ComposerEditorMentionSuggestionsProps, ComposerEditorProps, ComposerFloatingToolbarProps, ComposerFormProps, ComposerLinkProps, ComposerMarkToggleProps, ComposerMentionProps, ComposerSubmitComment, ComposerSubmitProps, ComposerSuggestionsListItemProps, ComposerSuggestionsListProps, index as EmojiPicker, EmojiPickerContentCategoryHeaderProps, EmojiPickerContentComponents, EmojiPickerContentEmojiProps, EmojiPickerContentEmptyProps, EmojiPickerContentErrorProps, EmojiPickerContentGridProps, EmojiPickerContentLoadingProps, EmojiPickerContentProps, EmojiPickerContentRowProps, EmojiPickerRootProps, EmojiPickerSearchProps, FileSize, FileSizeProps, Timestamp, TimestampProps, useComposer };
497
+ export { AttachmentTooLargeError, index$1 as Comment, CommentBodyComponents, CommentBodyLinkProps, CommentBodyMentionProps, CommentBodyProps, CommentLinkProps, CommentMentionProps, index as Composer, ComposerAttachFilesProps, ComposerAttachmentsDropAreaProps, ComposerBodyMark, ComposerBodyMarks, ComposerContext, ComposerEditorComponents, ComposerEditorFloatingToolbarProps, ComposerEditorLinkProps, ComposerEditorMentionProps, ComposerEditorMentionSuggestionsProps, ComposerEditorProps, ComposerFloatingToolbarProps, ComposerFormProps, ComposerLinkProps, ComposerMarkToggleProps, ComposerMentionProps, ComposerSubmitComment, ComposerSubmitProps, ComposerSuggestionsListItemProps, ComposerSuggestionsListProps, FileSize, FileSizeProps, Timestamp, TimestampProps, useComposer };