@liveblocks/react-ui 3.15.0 → 3.15.1-rc2

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 (64) hide show
  1. package/dist/_private/index.cjs +2 -0
  2. package/dist/_private/index.cjs.map +1 -1
  3. package/dist/_private/index.d.cts +3 -1
  4. package/dist/_private/index.d.ts +3 -1
  5. package/dist/_private/index.js +1 -0
  6. package/dist/_private/index.js.map +1 -1
  7. package/dist/components/AvatarStack.cjs +15 -10
  8. package/dist/components/AvatarStack.cjs.map +1 -1
  9. package/dist/components/AvatarStack.js +15 -10
  10. package/dist/components/AvatarStack.js.map +1 -1
  11. package/dist/components/CommentPin.cjs +4 -2
  12. package/dist/components/CommentPin.cjs.map +1 -1
  13. package/dist/components/CommentPin.js +4 -2
  14. package/dist/components/CommentPin.js.map +1 -1
  15. package/dist/components/Cursors.cjs +13 -16
  16. package/dist/components/Cursors.cjs.map +1 -1
  17. package/dist/components/Cursors.js +14 -17
  18. package/dist/components/Cursors.js.map +1 -1
  19. package/dist/components/FloatingComposer.cjs +14 -2
  20. package/dist/components/FloatingComposer.cjs.map +1 -1
  21. package/dist/components/FloatingComposer.js +15 -3
  22. package/dist/components/FloatingComposer.js.map +1 -1
  23. package/dist/components/FloatingThread.cjs +1 -0
  24. package/dist/components/FloatingThread.cjs.map +1 -1
  25. package/dist/components/FloatingThread.js +1 -0
  26. package/dist/components/FloatingThread.js.map +1 -1
  27. package/dist/components/internal/Dropdown.cjs +1 -0
  28. package/dist/components/internal/Dropdown.cjs.map +1 -1
  29. package/dist/components/internal/Dropdown.js +1 -0
  30. package/dist/components/internal/Dropdown.js.map +1 -1
  31. package/dist/components/internal/EmojiPicker.cjs +1 -0
  32. package/dist/components/internal/EmojiPicker.cjs.map +1 -1
  33. package/dist/components/internal/EmojiPicker.js +1 -0
  34. package/dist/components/internal/EmojiPicker.js.map +1 -1
  35. package/dist/components/internal/Tooltip.cjs +1 -0
  36. package/dist/components/internal/Tooltip.cjs.map +1 -1
  37. package/dist/components/internal/Tooltip.js +1 -0
  38. package/dist/components/internal/Tooltip.js.map +1 -1
  39. package/dist/icon.cjs +2 -0
  40. package/dist/icon.cjs.map +1 -1
  41. package/dist/icon.js +1 -0
  42. package/dist/icon.js.map +1 -1
  43. package/dist/icons/Plus.cjs +11 -0
  44. package/dist/icons/Plus.cjs.map +1 -0
  45. package/dist/icons/Plus.js +9 -0
  46. package/dist/icons/Plus.js.map +1 -0
  47. package/dist/icons/index.cjs +2 -0
  48. package/dist/icons/index.cjs.map +1 -1
  49. package/dist/icons/index.js +1 -0
  50. package/dist/icons/index.js.map +1 -1
  51. package/dist/index.d.cts +11 -3
  52. package/dist/index.d.ts +11 -3
  53. package/dist/utils/px.cjs +14 -0
  54. package/dist/utils/px.cjs.map +1 -0
  55. package/dist/utils/px.js +12 -0
  56. package/dist/utils/px.js.map +1 -0
  57. package/dist/version.cjs +1 -1
  58. package/dist/version.cjs.map +1 -1
  59. package/dist/version.js +1 -1
  60. package/dist/version.js.map +1 -1
  61. package/package.json +4 -4
  62. package/src/styles/index.css +8 -1
  63. package/styles.css +1 -1
  64. package/styles.css.map +1 -1
@@ -57,6 +57,7 @@ var ListOrdered = require('../icons/ListOrdered.cjs');
57
57
  var ListUnordered = require('../icons/ListUnordered.cjs');
58
58
  var Mention = require('../icons/Mention.cjs');
59
59
  var MinusCircle = require('../icons/MinusCircle.cjs');
60
+ var Plus = require('../icons/Plus.cjs');
60
61
  var QuestionMark = require('../icons/QuestionMark.cjs');
61
62
  var Redo = require('../icons/Redo.cjs');
62
63
  var Restore = require('../icons/Restore.cjs');
@@ -138,6 +139,7 @@ exports.ListOrderedIcon = ListOrdered.ListOrderedIcon;
138
139
  exports.ListUnorderedIcon = ListUnordered.ListUnorderedIcon;
139
140
  exports.MentionIcon = Mention.MentionIcon;
140
141
  exports.MinusCircleIcon = MinusCircle.MinusCircleIcon;
142
+ exports.PlusIcon = Plus.PlusIcon;
141
143
  exports.QuestionMarkIcon = QuestionMark.QuestionMarkIcon;
142
144
  exports.RedoIcon = Redo.RedoIcon;
143
145
  exports.RestoreIcon = Restore.RestoreIcon;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -447,6 +447,8 @@ declare function MentionIcon(props: ComponentProps<"svg">): react_jsx_runtime.JS
447
447
 
448
448
  declare function MinusCircleIcon(props: ComponentProps<"svg">): react_jsx_runtime.JSX.Element;
449
449
 
450
+ declare function PlusIcon(props: ComponentProps<"svg">): react_jsx_runtime.JSX.Element;
451
+
450
452
  declare function QuestionMarkIcon(props: ComponentProps<"svg">): react_jsx_runtime.JSX.Element;
451
453
 
452
454
  declare function RedoIcon(props: ComponentProps<"svg">): react_jsx_runtime.JSX.Element;
@@ -724,4 +726,4 @@ declare namespace index {
724
726
  };
725
727
  }
726
728
 
727
- export { index$2 as AiComposer, AiComposerEditorProps, AiComposerFormProps, AiComposerSubmitProps, index$1 as AiMessage, AiMessageContentComponents, AiMessageContentProps, AiMessageContentReasoningPartProps, AiMessageContentTextPartProps, ArrowCornerDownRightIcon, ArrowCornerUpRightIcon, ArrowDownIcon, ArrowUpIcon, AttachmentIcon, Avatar, BellCrossedIcon, BellIcon, BlockquoteIcon, BoldIcon, Button, CheckCircleFillIcon, CheckCircleIcon, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, CodeIcon, index as Collapsible, CommentIcon, CopyIcon, CrossCircleFillIcon, CrossIcon, DeleteIcon, EditIcon, EllipsisIcon, EmojiIcon, EmojiPlusIcon, GlobeIcon, Group, GroupDescription, H1Icon, H2Icon, H3Icon, ItalicIcon, LengthenIcon, List, ListOrderedIcon, ListUnorderedIcon, Markdown, MarkdownComponents, MarkdownComponentsBlockquoteProps, MarkdownComponentsCodeBlockProps, MarkdownComponentsHeadingProps, MarkdownComponentsImageProps, MarkdownComponentsInlineProps, MarkdownComponentsLinkProps, MarkdownComponentsListProps, MarkdownComponentsParagraphProps, MarkdownComponentsTableProps, MarkdownProps, MentionIcon, MinusCircleIcon, Portal, Prose, QuestionMarkIcon, RedoIcon, RestoreIcon, RetryIcon, SearchIcon, SelectButton, SendIcon, ShortcutTooltip, ShortenIcon, SparklesIcon, SparklesTextIcon, SpinnerIcon, StopIcon, StrikethroughIcon, TextIcon, Tooltip, TooltipProvider, TranslateIcon, UnderlineIcon, UndoIcon, User, UserIcon, UsersIcon, WarningIcon, capitalize, cn, useAiComposer, useInitial, useLiveblocksUiConfig, useRefs, useStableComponent };
729
+ export { index$2 as AiComposer, AiComposerEditorProps, AiComposerFormProps, AiComposerSubmitProps, index$1 as AiMessage, AiMessageContentComponents, AiMessageContentProps, AiMessageContentReasoningPartProps, AiMessageContentTextPartProps, ArrowCornerDownRightIcon, ArrowCornerUpRightIcon, ArrowDownIcon, ArrowUpIcon, AttachmentIcon, Avatar, BellCrossedIcon, BellIcon, BlockquoteIcon, BoldIcon, Button, CheckCircleFillIcon, CheckCircleIcon, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, CodeIcon, index as Collapsible, CommentIcon, CopyIcon, CrossCircleFillIcon, CrossIcon, DeleteIcon, EditIcon, EllipsisIcon, EmojiIcon, EmojiPlusIcon, GlobeIcon, Group, GroupDescription, H1Icon, H2Icon, H3Icon, ItalicIcon, LengthenIcon, List, ListOrderedIcon, ListUnorderedIcon, Markdown, MarkdownComponents, MarkdownComponentsBlockquoteProps, MarkdownComponentsCodeBlockProps, MarkdownComponentsHeadingProps, MarkdownComponentsImageProps, MarkdownComponentsInlineProps, MarkdownComponentsLinkProps, MarkdownComponentsListProps, MarkdownComponentsParagraphProps, MarkdownComponentsTableProps, MarkdownProps, MentionIcon, MinusCircleIcon, PlusIcon, Portal, Prose, QuestionMarkIcon, RedoIcon, RestoreIcon, RetryIcon, SearchIcon, SelectButton, SendIcon, ShortcutTooltip, ShortenIcon, SparklesIcon, SparklesTextIcon, SpinnerIcon, StopIcon, StrikethroughIcon, TextIcon, Tooltip, TooltipProvider, TranslateIcon, UnderlineIcon, UndoIcon, User, UserIcon, UsersIcon, WarningIcon, capitalize, cn, useAiComposer, useInitial, useLiveblocksUiConfig, useRefs, useStableComponent };
@@ -447,6 +447,8 @@ declare function MentionIcon(props: ComponentProps<"svg">): react_jsx_runtime.JS
447
447
 
448
448
  declare function MinusCircleIcon(props: ComponentProps<"svg">): react_jsx_runtime.JSX.Element;
449
449
 
450
+ declare function PlusIcon(props: ComponentProps<"svg">): react_jsx_runtime.JSX.Element;
451
+
450
452
  declare function QuestionMarkIcon(props: ComponentProps<"svg">): react_jsx_runtime.JSX.Element;
451
453
 
452
454
  declare function RedoIcon(props: ComponentProps<"svg">): react_jsx_runtime.JSX.Element;
@@ -724,4 +726,4 @@ declare namespace index {
724
726
  };
725
727
  }
726
728
 
727
- export { index$2 as AiComposer, AiComposerEditorProps, AiComposerFormProps, AiComposerSubmitProps, index$1 as AiMessage, AiMessageContentComponents, AiMessageContentProps, AiMessageContentReasoningPartProps, AiMessageContentTextPartProps, ArrowCornerDownRightIcon, ArrowCornerUpRightIcon, ArrowDownIcon, ArrowUpIcon, AttachmentIcon, Avatar, BellCrossedIcon, BellIcon, BlockquoteIcon, BoldIcon, Button, CheckCircleFillIcon, CheckCircleIcon, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, CodeIcon, index as Collapsible, CommentIcon, CopyIcon, CrossCircleFillIcon, CrossIcon, DeleteIcon, EditIcon, EllipsisIcon, EmojiIcon, EmojiPlusIcon, GlobeIcon, Group, GroupDescription, H1Icon, H2Icon, H3Icon, ItalicIcon, LengthenIcon, List, ListOrderedIcon, ListUnorderedIcon, Markdown, MarkdownComponents, MarkdownComponentsBlockquoteProps, MarkdownComponentsCodeBlockProps, MarkdownComponentsHeadingProps, MarkdownComponentsImageProps, MarkdownComponentsInlineProps, MarkdownComponentsLinkProps, MarkdownComponentsListProps, MarkdownComponentsParagraphProps, MarkdownComponentsTableProps, MarkdownProps, MentionIcon, MinusCircleIcon, Portal, Prose, QuestionMarkIcon, RedoIcon, RestoreIcon, RetryIcon, SearchIcon, SelectButton, SendIcon, ShortcutTooltip, ShortenIcon, SparklesIcon, SparklesTextIcon, SpinnerIcon, StopIcon, StrikethroughIcon, TextIcon, Tooltip, TooltipProvider, TranslateIcon, UnderlineIcon, UndoIcon, User, UserIcon, UsersIcon, WarningIcon, capitalize, cn, useAiComposer, useInitial, useLiveblocksUiConfig, useRefs, useStableComponent };
729
+ export { index$2 as AiComposer, AiComposerEditorProps, AiComposerFormProps, AiComposerSubmitProps, index$1 as AiMessage, AiMessageContentComponents, AiMessageContentProps, AiMessageContentReasoningPartProps, AiMessageContentTextPartProps, ArrowCornerDownRightIcon, ArrowCornerUpRightIcon, ArrowDownIcon, ArrowUpIcon, AttachmentIcon, Avatar, BellCrossedIcon, BellIcon, BlockquoteIcon, BoldIcon, Button, CheckCircleFillIcon, CheckCircleIcon, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, CodeIcon, index as Collapsible, CommentIcon, CopyIcon, CrossCircleFillIcon, CrossIcon, DeleteIcon, EditIcon, EllipsisIcon, EmojiIcon, EmojiPlusIcon, GlobeIcon, Group, GroupDescription, H1Icon, H2Icon, H3Icon, ItalicIcon, LengthenIcon, List, ListOrderedIcon, ListUnorderedIcon, Markdown, MarkdownComponents, MarkdownComponentsBlockquoteProps, MarkdownComponentsCodeBlockProps, MarkdownComponentsHeadingProps, MarkdownComponentsImageProps, MarkdownComponentsInlineProps, MarkdownComponentsLinkProps, MarkdownComponentsListProps, MarkdownComponentsParagraphProps, MarkdownComponentsTableProps, MarkdownProps, MentionIcon, MinusCircleIcon, PlusIcon, Portal, Prose, QuestionMarkIcon, RedoIcon, RestoreIcon, RetryIcon, SearchIcon, SelectButton, SendIcon, ShortcutTooltip, ShortenIcon, SparklesIcon, SparklesTextIcon, SpinnerIcon, StopIcon, StrikethroughIcon, TextIcon, Tooltip, TooltipProvider, TranslateIcon, UnderlineIcon, UndoIcon, User, UserIcon, UsersIcon, WarningIcon, capitalize, cn, useAiComposer, useInitial, useLiveblocksUiConfig, useRefs, useStableComponent };
@@ -58,6 +58,7 @@ export { ListOrderedIcon } from '../icons/ListOrdered.js';
58
58
  export { ListUnorderedIcon } from '../icons/ListUnordered.js';
59
59
  export { MentionIcon } from '../icons/Mention.js';
60
60
  export { MinusCircleIcon } from '../icons/MinusCircle.js';
61
+ export { PlusIcon } from '../icons/Plus.js';
61
62
  export { QuestionMarkIcon } from '../icons/QuestionMark.js';
62
63
  export { RedoIcon } from '../icons/Redo.js';
63
64
  export { RestoreIcon } from '../icons/Restore.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -7,6 +7,7 @@ var react = require('react');
7
7
  var constants = require('../constants.cjs');
8
8
  var overrides = require('../overrides.cjs');
9
9
  var cn = require('../utils/cn.cjs');
10
+ var px = require('../utils/px.cjs');
10
11
  var Avatar = require('./internal/Avatar.cjs');
11
12
  var Tooltip = require('./internal/Tooltip.cjs');
12
13
  var User = require('./internal/User.cjs');
@@ -23,18 +24,22 @@ const AvatarStack = react.forwardRef(
23
24
  ...props
24
25
  }, forwardedRef) => {
25
26
  const $ = overrides.useOverrides(overrides$1);
26
- const otherIds = react$1.useOthers((others) => others.map((user) => user.id));
27
+ const otherIds = react$1.useOthers(
28
+ (others) => [...others].sort((a, b) => b.connectionId - a.connectionId).map((user) => user.id)
29
+ );
27
30
  const selfId = react$1.useSelf((self) => self.id);
28
31
  const userIds = react.useMemo(() => {
29
- const uniqueUserIds = /* @__PURE__ */ new Set([
30
- selfId,
31
- ...otherIds,
32
- ...additionalUserIds
33
- ]);
32
+ const uniqueUserIds = new Set(
33
+ [selfId, ...otherIds, ...additionalUserIds].filter(
34
+ (userId) => userId !== null
35
+ )
36
+ );
34
37
  return [...uniqueUserIds];
35
38
  }, [selfId, otherIds, additionalUserIds]);
36
- const maxAvatars = Math.max(1, Math.floor(max));
37
- const visibleUserIds = userIds.slice(0, maxAvatars);
39
+ const maxItems = max === null ? Infinity : Math.max(2, Math.floor(max));
40
+ const shouldShowMore = userIds.length > maxItems;
41
+ const visibleAvatarsCount = shouldShowMore ? maxItems - 1 : maxItems;
42
+ const visibleUserIds = userIds.slice(0, visibleAvatarsCount);
38
43
  const hiddenUserIds = userIds.slice(visibleUserIds.length);
39
44
  const remainingUsersCount = hiddenUserIds.length;
40
45
  const visibleItemsCount = visibleUserIds.length + Number(remainingUsersCount > 0);
@@ -47,8 +52,8 @@ const AvatarStack = react.forwardRef(
47
52
  className: cn.cn("lb-root lb-avatar-stack", className),
48
53
  dir: $.dir,
49
54
  style: {
50
- "--lb-avatar-stack-count": visibleItemsCount - 1,
51
- "--lb-avatar-stack-size": size,
55
+ "--lb-avatar-stack-count": visibleItemsCount,
56
+ "--lb-avatar-stack-size": px.px(size),
52
57
  ...style
53
58
  },
54
59
  ...props,
@@ -1 +1 @@
1
- {"version":3,"file":"AvatarStack.cjs","sources":["../../src/components/AvatarStack.tsx"],"sourcesContent":["\"use client\";\n\nimport { useOthers, useSelf } from \"@liveblocks/react\";\nimport type { ComponentPropsWithoutRef, CSSProperties } from \"react\";\nimport { forwardRef, useMemo } from \"react\";\n\nimport {\n FLOATING_ELEMENT_COLLISION_PADDING,\n FLOATING_ELEMENT_SIDE_OFFSET,\n} from \"../constants\";\nimport type { GlobalOverrides } from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport { cn } from \"../utils/cn\";\nimport { Avatar } from \"./internal/Avatar\";\nimport { Tooltip, TooltipProvider } from \"./internal/Tooltip\";\nimport { User } from \"./internal/User\";\n\nexport interface AvatarStackProps extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * Optional additional user IDs to include in the stack.\n */\n userIds?: string[];\n\n /**\n * The maximum number of visible avatars.\n * Defaults to 3, set to `undefined` to show all avatars.\n */\n max?: number;\n\n /**\n * The size of the avatars.\n */\n size?: string | number;\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides>;\n}\n\n/**\n * Displays a stack of avatars for the users currently present in the room.\n */\nexport const AvatarStack = forwardRef<HTMLDivElement, AvatarStackProps>(\n (\n {\n userIds: additionalUserIds = [],\n max = 3,\n size,\n overrides,\n className,\n style,\n ...props\n },\n forwardedRef\n ) => {\n const $ = useOverrides(overrides);\n const otherIds = useOthers((others) => others.map((user) => user.id));\n const selfId = useSelf((self) => self.id);\n const userIds = useMemo(() => {\n const uniqueUserIds = new Set([\n selfId,\n ...otherIds,\n ...additionalUserIds,\n ]);\n\n return [...uniqueUserIds];\n }, [selfId, otherIds, additionalUserIds]);\n const maxAvatars = Math.max(1, Math.floor(max));\n const visibleUserIds = userIds.slice(0, maxAvatars);\n const hiddenUserIds = userIds.slice(visibleUserIds.length);\n const remainingUsersCount = hiddenUserIds.length;\n const visibleItemsCount =\n visibleUserIds.length + Number(remainingUsersCount > 0);\n\n if (userIds.length === 0) {\n return null;\n }\n\n return (\n <TooltipProvider>\n <div\n className={cn(\"lb-root lb-avatar-stack\", className)}\n dir={$.dir}\n style={\n {\n \"--lb-avatar-stack-count\": visibleItemsCount - 1,\n \"--lb-avatar-stack-size\": size,\n ...style,\n } as CSSProperties\n }\n {...props}\n ref={forwardedRef}\n >\n {visibleUserIds.map((userId, index) => {\n if (!userId) {\n return null;\n }\n\n return (\n <Tooltip\n key={userId}\n content={<User userId={userId} />}\n sideOffset={FLOATING_ELEMENT_SIDE_OFFSET}\n collisionPadding={FLOATING_ELEMENT_COLLISION_PADDING}\n side=\"top\"\n align=\"center\"\n >\n <Avatar\n userId={userId}\n className=\"lb-avatar-stack-avatar\"\n style={{ \"--lb-avatar-stack-index\": index } as CSSProperties}\n />\n </Tooltip>\n );\n })}\n {remainingUsersCount > 0 ? (\n <Tooltip\n content={\n <ul className=\"lb-users-tooltip-list\">\n {hiddenUserIds.map((userId) =>\n userId ? (\n <li key={userId} className=\"lb-users-tooltip-list-item\">\n <Avatar userId={userId} />\n <User userId={userId} />\n </li>\n ) : null\n )}\n </ul>\n }\n sideOffset={FLOATING_ELEMENT_SIDE_OFFSET}\n collisionPadding={FLOATING_ELEMENT_COLLISION_PADDING}\n side=\"top\"\n align=\"center\"\n className=\"lb-users-tooltip\"\n >\n <div\n className=\"lb-avatar lb-avatar-stack-avatar lb-avatar-stack-more\"\n style={\n {\n \"--lb-avatar-stack-index\": visibleUserIds.length,\n } as CSSProperties\n }\n >\n <span className=\"lb-avatar-fallback\">\n +{remainingUsersCount}\n </span>\n </div>\n </Tooltip>\n ) : null}\n </div>\n </TooltipProvider>\n );\n }\n);\n"],"names":[],"mappings":";;;;;;;;;;;;;;AA2CO;AAAoB;AAEvB;AACgC;AACxB;AACN;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACE;AAA8B;AAC5B;AACG;AACA;AAGL;AAAwB;AAE1B;AACA;AACA;AACA;AACA;AAGA;AACE;AAAO;AAGT;AAEI;AAAC;AAAA;AACmD;AAC3C;AAEL;AACiD;AACrB;AACvB;AACL;AAEE;AACC;AAEJ;AACC;AACE;AAAO;AAGT;AACE;AAAC;AAAA;AAEgC;AACnB;AACM;AACb;AACC;AAEN;AAAC;AAAA;AACC;AACU;AACgC;AAAA;AAC5C;AAAA;AAXK;AAYP;AAEH;AAEC;AAAC;AAAA;AAGoB;AAGT;AAAwB;AACF;AAEtB;AAER;AAEU;AACM;AACb;AACC;AACI;AAEV;AAAC;AAAA;AACW;AAER;AAC4C;AAC5C;AAGmC;AAAA;AACjC;AACJ;AAAA;AACF;AAAA;AAEA;AAAA;AAAA;AAER;AAGN;;"}
1
+ {"version":3,"file":"AvatarStack.cjs","sources":["../../src/components/AvatarStack.tsx"],"sourcesContent":["\"use client\";\n\nimport { useOthers, useSelf } from \"@liveblocks/react\";\nimport type { ComponentPropsWithoutRef, CSSProperties } from \"react\";\nimport { forwardRef, useMemo } from \"react\";\n\nimport {\n FLOATING_ELEMENT_COLLISION_PADDING,\n FLOATING_ELEMENT_SIDE_OFFSET,\n} from \"../constants\";\nimport type { GlobalOverrides } from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport { cn } from \"../utils/cn\";\nimport { px } from \"../utils/px\";\nimport { Avatar } from \"./internal/Avatar\";\nimport { Tooltip, TooltipProvider } from \"./internal/Tooltip\";\nimport { User } from \"./internal/User\";\n\nexport interface AvatarStackProps extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * Optional additional user IDs to include in the stack.\n */\n userIds?: string[];\n\n /**\n * The maximum number of items in the stack (at least 2).\n * Defaults to 3, set to `null` to show all avatars.\n */\n max?: number | null;\n\n /**\n * The size of the avatars.\n */\n size?: string | number;\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides>;\n}\n\n/**\n * Displays a stack of avatars for the users currently present in the room.\n */\nexport const AvatarStack = forwardRef<HTMLDivElement, AvatarStackProps>(\n (\n {\n userIds: additionalUserIds = [],\n max = 3,\n size,\n overrides,\n className,\n style,\n ...props\n },\n forwardedRef\n ) => {\n const $ = useOverrides(overrides);\n const otherIds = useOthers((others) =>\n [...others]\n .sort((a, b) => b.connectionId - a.connectionId)\n .map((user) => user.id)\n );\n const selfId = useSelf((self) => self.id);\n const userIds = useMemo(() => {\n const uniqueUserIds = new Set(\n [selfId, ...otherIds, ...additionalUserIds].filter(\n (userId): userId is string => userId !== null\n )\n );\n\n return [...uniqueUserIds];\n }, [selfId, otherIds, additionalUserIds]);\n const maxItems = max === null ? Infinity : Math.max(2, Math.floor(max));\n const shouldShowMore = userIds.length > maxItems;\n const visibleAvatarsCount = shouldShowMore ? maxItems - 1 : maxItems;\n const visibleUserIds = userIds.slice(0, visibleAvatarsCount);\n const hiddenUserIds = userIds.slice(visibleUserIds.length);\n const remainingUsersCount = hiddenUserIds.length;\n const visibleItemsCount =\n visibleUserIds.length + Number(remainingUsersCount > 0);\n\n if (userIds.length === 0) {\n return null;\n }\n\n return (\n <TooltipProvider>\n <div\n className={cn(\"lb-root lb-avatar-stack\", className)}\n dir={$.dir}\n style={\n {\n \"--lb-avatar-stack-count\": visibleItemsCount,\n \"--lb-avatar-stack-size\": px(size),\n ...style,\n } as CSSProperties\n }\n {...props}\n ref={forwardedRef}\n >\n {visibleUserIds.map((userId, index) => {\n if (!userId) {\n return null;\n }\n\n return (\n <Tooltip\n key={userId}\n content={<User userId={userId} />}\n sideOffset={FLOATING_ELEMENT_SIDE_OFFSET}\n collisionPadding={FLOATING_ELEMENT_COLLISION_PADDING}\n side=\"top\"\n align=\"center\"\n >\n <Avatar\n userId={userId}\n className=\"lb-avatar-stack-avatar\"\n style={{ \"--lb-avatar-stack-index\": index } as CSSProperties}\n />\n </Tooltip>\n );\n })}\n {remainingUsersCount > 0 ? (\n <Tooltip\n content={\n <ul className=\"lb-users-tooltip-list\">\n {hiddenUserIds.map((userId) =>\n userId ? (\n <li key={userId} className=\"lb-users-tooltip-list-item\">\n <Avatar userId={userId} />\n <User userId={userId} />\n </li>\n ) : null\n )}\n </ul>\n }\n sideOffset={FLOATING_ELEMENT_SIDE_OFFSET}\n collisionPadding={FLOATING_ELEMENT_COLLISION_PADDING}\n side=\"top\"\n align=\"center\"\n className=\"lb-users-tooltip\"\n >\n <div\n className=\"lb-avatar lb-avatar-stack-avatar lb-avatar-stack-more\"\n style={\n {\n \"--lb-avatar-stack-index\": visibleUserIds.length,\n } as CSSProperties\n }\n >\n <span className=\"lb-avatar-fallback\">\n +{remainingUsersCount}\n </span>\n </div>\n </Tooltip>\n ) : null}\n </div>\n </TooltipProvider>\n );\n }\n);\n"],"names":[],"mappings":";;;;;;;;;;;;;;;AA4CO;AAAoB;AAEvB;AACgC;AACxB;AACN;AACA;AACA;AACA;AACG;AAIL;AACA;AAAiB;AAGS;AAE1B;AACA;AACE;AAA0B;AACoB;AACD;AAC3C;AAGF;AAAwB;AAE1B;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACE;AAAO;AAGT;AAEI;AAAC;AAAA;AACmD;AAC3C;AAEL;AAC6B;AACM;AAC9B;AACL;AAEE;AACC;AAEJ;AACC;AACE;AAAO;AAGT;AACE;AAAC;AAAA;AAEgC;AACnB;AACM;AACb;AACC;AAEN;AAAC;AAAA;AACC;AACU;AACgC;AAAA;AAC5C;AAAA;AAXK;AAYP;AAEH;AAEC;AAAC;AAAA;AAGoB;AAGT;AAAwB;AACF;AAEtB;AAER;AAEU;AACM;AACb;AACC;AACI;AAEV;AAAC;AAAA;AACW;AAER;AAC4C;AAC5C;AAGmC;AAAA;AACjC;AACJ;AAAA;AACF;AAAA;AAEA;AAAA;AAAA;AAER;AAGN;;"}
@@ -5,6 +5,7 @@ import { forwardRef, useMemo } from 'react';
5
5
  import { FLOATING_ELEMENT_SIDE_OFFSET, FLOATING_ELEMENT_COLLISION_PADDING } from '../constants.js';
6
6
  import { useOverrides } from '../overrides.js';
7
7
  import { cn } from '../utils/cn.js';
8
+ import { px } from '../utils/px.js';
8
9
  import { Avatar } from './internal/Avatar.js';
9
10
  import { TooltipProvider, Tooltip } from './internal/Tooltip.js';
10
11
  import { User } from './internal/User.js';
@@ -21,18 +22,22 @@ const AvatarStack = forwardRef(
21
22
  ...props
22
23
  }, forwardedRef) => {
23
24
  const $ = useOverrides(overrides);
24
- const otherIds = useOthers((others) => others.map((user) => user.id));
25
+ const otherIds = useOthers(
26
+ (others) => [...others].sort((a, b) => b.connectionId - a.connectionId).map((user) => user.id)
27
+ );
25
28
  const selfId = useSelf((self) => self.id);
26
29
  const userIds = useMemo(() => {
27
- const uniqueUserIds = /* @__PURE__ */ new Set([
28
- selfId,
29
- ...otherIds,
30
- ...additionalUserIds
31
- ]);
30
+ const uniqueUserIds = new Set(
31
+ [selfId, ...otherIds, ...additionalUserIds].filter(
32
+ (userId) => userId !== null
33
+ )
34
+ );
32
35
  return [...uniqueUserIds];
33
36
  }, [selfId, otherIds, additionalUserIds]);
34
- const maxAvatars = Math.max(1, Math.floor(max));
35
- const visibleUserIds = userIds.slice(0, maxAvatars);
37
+ const maxItems = max === null ? Infinity : Math.max(2, Math.floor(max));
38
+ const shouldShowMore = userIds.length > maxItems;
39
+ const visibleAvatarsCount = shouldShowMore ? maxItems - 1 : maxItems;
40
+ const visibleUserIds = userIds.slice(0, visibleAvatarsCount);
36
41
  const hiddenUserIds = userIds.slice(visibleUserIds.length);
37
42
  const remainingUsersCount = hiddenUserIds.length;
38
43
  const visibleItemsCount = visibleUserIds.length + Number(remainingUsersCount > 0);
@@ -45,8 +50,8 @@ const AvatarStack = forwardRef(
45
50
  className: cn("lb-root lb-avatar-stack", className),
46
51
  dir: $.dir,
47
52
  style: {
48
- "--lb-avatar-stack-count": visibleItemsCount - 1,
49
- "--lb-avatar-stack-size": size,
53
+ "--lb-avatar-stack-count": visibleItemsCount,
54
+ "--lb-avatar-stack-size": px(size),
50
55
  ...style
51
56
  },
52
57
  ...props,
@@ -1 +1 @@
1
- {"version":3,"file":"AvatarStack.js","sources":["../../src/components/AvatarStack.tsx"],"sourcesContent":["\"use client\";\n\nimport { useOthers, useSelf } from \"@liveblocks/react\";\nimport type { ComponentPropsWithoutRef, CSSProperties } from \"react\";\nimport { forwardRef, useMemo } from \"react\";\n\nimport {\n FLOATING_ELEMENT_COLLISION_PADDING,\n FLOATING_ELEMENT_SIDE_OFFSET,\n} from \"../constants\";\nimport type { GlobalOverrides } from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport { cn } from \"../utils/cn\";\nimport { Avatar } from \"./internal/Avatar\";\nimport { Tooltip, TooltipProvider } from \"./internal/Tooltip\";\nimport { User } from \"./internal/User\";\n\nexport interface AvatarStackProps extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * Optional additional user IDs to include in the stack.\n */\n userIds?: string[];\n\n /**\n * The maximum number of visible avatars.\n * Defaults to 3, set to `undefined` to show all avatars.\n */\n max?: number;\n\n /**\n * The size of the avatars.\n */\n size?: string | number;\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides>;\n}\n\n/**\n * Displays a stack of avatars for the users currently present in the room.\n */\nexport const AvatarStack = forwardRef<HTMLDivElement, AvatarStackProps>(\n (\n {\n userIds: additionalUserIds = [],\n max = 3,\n size,\n overrides,\n className,\n style,\n ...props\n },\n forwardedRef\n ) => {\n const $ = useOverrides(overrides);\n const otherIds = useOthers((others) => others.map((user) => user.id));\n const selfId = useSelf((self) => self.id);\n const userIds = useMemo(() => {\n const uniqueUserIds = new Set([\n selfId,\n ...otherIds,\n ...additionalUserIds,\n ]);\n\n return [...uniqueUserIds];\n }, [selfId, otherIds, additionalUserIds]);\n const maxAvatars = Math.max(1, Math.floor(max));\n const visibleUserIds = userIds.slice(0, maxAvatars);\n const hiddenUserIds = userIds.slice(visibleUserIds.length);\n const remainingUsersCount = hiddenUserIds.length;\n const visibleItemsCount =\n visibleUserIds.length + Number(remainingUsersCount > 0);\n\n if (userIds.length === 0) {\n return null;\n }\n\n return (\n <TooltipProvider>\n <div\n className={cn(\"lb-root lb-avatar-stack\", className)}\n dir={$.dir}\n style={\n {\n \"--lb-avatar-stack-count\": visibleItemsCount - 1,\n \"--lb-avatar-stack-size\": size,\n ...style,\n } as CSSProperties\n }\n {...props}\n ref={forwardedRef}\n >\n {visibleUserIds.map((userId, index) => {\n if (!userId) {\n return null;\n }\n\n return (\n <Tooltip\n key={userId}\n content={<User userId={userId} />}\n sideOffset={FLOATING_ELEMENT_SIDE_OFFSET}\n collisionPadding={FLOATING_ELEMENT_COLLISION_PADDING}\n side=\"top\"\n align=\"center\"\n >\n <Avatar\n userId={userId}\n className=\"lb-avatar-stack-avatar\"\n style={{ \"--lb-avatar-stack-index\": index } as CSSProperties}\n />\n </Tooltip>\n );\n })}\n {remainingUsersCount > 0 ? (\n <Tooltip\n content={\n <ul className=\"lb-users-tooltip-list\">\n {hiddenUserIds.map((userId) =>\n userId ? (\n <li key={userId} className=\"lb-users-tooltip-list-item\">\n <Avatar userId={userId} />\n <User userId={userId} />\n </li>\n ) : null\n )}\n </ul>\n }\n sideOffset={FLOATING_ELEMENT_SIDE_OFFSET}\n collisionPadding={FLOATING_ELEMENT_COLLISION_PADDING}\n side=\"top\"\n align=\"center\"\n className=\"lb-users-tooltip\"\n >\n <div\n className=\"lb-avatar lb-avatar-stack-avatar lb-avatar-stack-more\"\n style={\n {\n \"--lb-avatar-stack-index\": visibleUserIds.length,\n } as CSSProperties\n }\n >\n <span className=\"lb-avatar-fallback\">\n +{remainingUsersCount}\n </span>\n </div>\n </Tooltip>\n ) : null}\n </div>\n </TooltipProvider>\n );\n }\n);\n"],"names":[],"mappings":";;;;;;;;;;;;AA2CO;AAAoB;AAEvB;AACgC;AACxB;AACN;AACA;AACA;AACA;AACG;AAIL;AACA;AACA;AACA;AACE;AAA8B;AAC5B;AACG;AACA;AAGL;AAAwB;AAE1B;AACA;AACA;AACA;AACA;AAGA;AACE;AAAO;AAGT;AAEI;AAAC;AAAA;AACmD;AAC3C;AAEL;AACiD;AACrB;AACvB;AACL;AAEE;AACC;AAEJ;AACC;AACE;AAAO;AAGT;AACE;AAAC;AAAA;AAEgC;AACnB;AACM;AACb;AACC;AAEN;AAAC;AAAA;AACC;AACU;AACgC;AAAA;AAC5C;AAAA;AAXK;AAYP;AAEH;AAEC;AAAC;AAAA;AAGoB;AAGT;AAAwB;AACF;AAEtB;AAER;AAEU;AACM;AACb;AACC;AACI;AAEV;AAAC;AAAA;AACW;AAER;AAC4C;AAC5C;AAGmC;AAAA;AACjC;AACJ;AAAA;AACF;AAAA;AAEA;AAAA;AAAA;AAER;AAGN;;"}
1
+ {"version":3,"file":"AvatarStack.js","sources":["../../src/components/AvatarStack.tsx"],"sourcesContent":["\"use client\";\n\nimport { useOthers, useSelf } from \"@liveblocks/react\";\nimport type { ComponentPropsWithoutRef, CSSProperties } from \"react\";\nimport { forwardRef, useMemo } from \"react\";\n\nimport {\n FLOATING_ELEMENT_COLLISION_PADDING,\n FLOATING_ELEMENT_SIDE_OFFSET,\n} from \"../constants\";\nimport type { GlobalOverrides } from \"../overrides\";\nimport { useOverrides } from \"../overrides\";\nimport { cn } from \"../utils/cn\";\nimport { px } from \"../utils/px\";\nimport { Avatar } from \"./internal/Avatar\";\nimport { Tooltip, TooltipProvider } from \"./internal/Tooltip\";\nimport { User } from \"./internal/User\";\n\nexport interface AvatarStackProps extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * Optional additional user IDs to include in the stack.\n */\n userIds?: string[];\n\n /**\n * The maximum number of items in the stack (at least 2).\n * Defaults to 3, set to `null` to show all avatars.\n */\n max?: number | null;\n\n /**\n * The size of the avatars.\n */\n size?: string | number;\n\n /**\n * Override the component's strings.\n */\n overrides?: Partial<GlobalOverrides>;\n}\n\n/**\n * Displays a stack of avatars for the users currently present in the room.\n */\nexport const AvatarStack = forwardRef<HTMLDivElement, AvatarStackProps>(\n (\n {\n userIds: additionalUserIds = [],\n max = 3,\n size,\n overrides,\n className,\n style,\n ...props\n },\n forwardedRef\n ) => {\n const $ = useOverrides(overrides);\n const otherIds = useOthers((others) =>\n [...others]\n .sort((a, b) => b.connectionId - a.connectionId)\n .map((user) => user.id)\n );\n const selfId = useSelf((self) => self.id);\n const userIds = useMemo(() => {\n const uniqueUserIds = new Set(\n [selfId, ...otherIds, ...additionalUserIds].filter(\n (userId): userId is string => userId !== null\n )\n );\n\n return [...uniqueUserIds];\n }, [selfId, otherIds, additionalUserIds]);\n const maxItems = max === null ? Infinity : Math.max(2, Math.floor(max));\n const shouldShowMore = userIds.length > maxItems;\n const visibleAvatarsCount = shouldShowMore ? maxItems - 1 : maxItems;\n const visibleUserIds = userIds.slice(0, visibleAvatarsCount);\n const hiddenUserIds = userIds.slice(visibleUserIds.length);\n const remainingUsersCount = hiddenUserIds.length;\n const visibleItemsCount =\n visibleUserIds.length + Number(remainingUsersCount > 0);\n\n if (userIds.length === 0) {\n return null;\n }\n\n return (\n <TooltipProvider>\n <div\n className={cn(\"lb-root lb-avatar-stack\", className)}\n dir={$.dir}\n style={\n {\n \"--lb-avatar-stack-count\": visibleItemsCount,\n \"--lb-avatar-stack-size\": px(size),\n ...style,\n } as CSSProperties\n }\n {...props}\n ref={forwardedRef}\n >\n {visibleUserIds.map((userId, index) => {\n if (!userId) {\n return null;\n }\n\n return (\n <Tooltip\n key={userId}\n content={<User userId={userId} />}\n sideOffset={FLOATING_ELEMENT_SIDE_OFFSET}\n collisionPadding={FLOATING_ELEMENT_COLLISION_PADDING}\n side=\"top\"\n align=\"center\"\n >\n <Avatar\n userId={userId}\n className=\"lb-avatar-stack-avatar\"\n style={{ \"--lb-avatar-stack-index\": index } as CSSProperties}\n />\n </Tooltip>\n );\n })}\n {remainingUsersCount > 0 ? (\n <Tooltip\n content={\n <ul className=\"lb-users-tooltip-list\">\n {hiddenUserIds.map((userId) =>\n userId ? (\n <li key={userId} className=\"lb-users-tooltip-list-item\">\n <Avatar userId={userId} />\n <User userId={userId} />\n </li>\n ) : null\n )}\n </ul>\n }\n sideOffset={FLOATING_ELEMENT_SIDE_OFFSET}\n collisionPadding={FLOATING_ELEMENT_COLLISION_PADDING}\n side=\"top\"\n align=\"center\"\n className=\"lb-users-tooltip\"\n >\n <div\n className=\"lb-avatar lb-avatar-stack-avatar lb-avatar-stack-more\"\n style={\n {\n \"--lb-avatar-stack-index\": visibleUserIds.length,\n } as CSSProperties\n }\n >\n <span className=\"lb-avatar-fallback\">\n +{remainingUsersCount}\n </span>\n </div>\n </Tooltip>\n ) : null}\n </div>\n </TooltipProvider>\n );\n }\n);\n"],"names":[],"mappings":";;;;;;;;;;;;;AA4CO;AAAoB;AAEvB;AACgC;AACxB;AACN;AACA;AACA;AACA;AACG;AAIL;AACA;AAAiB;AAGS;AAE1B;AACA;AACE;AAA0B;AACoB;AACD;AAC3C;AAGF;AAAwB;AAE1B;AACA;AACA;AACA;AACA;AACA;AACA;AAGA;AACE;AAAO;AAGT;AAEI;AAAC;AAAA;AACmD;AAC3C;AAEL;AAC6B;AACM;AAC9B;AACL;AAEE;AACC;AAEJ;AACC;AACE;AAAO;AAGT;AACE;AAAC;AAAA;AAEgC;AACnB;AACM;AACb;AACC;AAEN;AAAC;AAAA;AACC;AACU;AACgC;AAAA;AAC5C;AAAA;AAXK;AAYP;AAEH;AAEC;AAAC;AAAA;AAGoB;AAGT;AAAwB;AACF;AAEtB;AAER;AAEU;AACM;AACb;AACC;AACI;AAEV;AAAC;AAAA;AACW;AAER;AAC4C;AAC5C;AAGmC;AAAA;AACjC;AACJ;AAAA;AACF;AAAA;AAEA;AAAA;AAAA;AAER;AAGN;;"}
@@ -4,6 +4,7 @@
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  var react = require('react');
6
6
  var cn = require('../utils/cn.cjs');
7
+ var px = require('../utils/px.cjs');
7
8
  var Avatar = require('./internal/Avatar.cjs');
8
9
 
9
10
 
@@ -15,6 +16,7 @@ const CommentPin = react.forwardRef(
15
16
  type = "button",
16
17
  className,
17
18
  style,
19
+ children,
18
20
  ...props
19
21
  }, forwardedRef) => {
20
22
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -22,11 +24,11 @@ const CommentPin = react.forwardRef(
22
24
  {
23
25
  className: cn.cn("lb-root lb-comment-pin", className),
24
26
  "data-corner": corner,
25
- style: { "--lb-comment-pin-size": size, ...style },
27
+ style: { "--lb-comment-pin-size": px.px(size), ...style },
26
28
  type,
27
29
  ...props,
28
30
  ref: forwardedRef,
29
- children: userId ? /* @__PURE__ */ jsxRuntime.jsx(Avatar.Avatar, { className: "lb-comment-pin-avatar", userId }) : null
31
+ children: children ?? (userId ? /* @__PURE__ */ jsxRuntime.jsx(Avatar.Avatar, { className: "lb-comment-pin-avatar", userId }) : null)
30
32
  }
31
33
  );
32
34
  }
@@ -1 +1 @@
1
- {"version":3,"file":"CommentPin.cjs","sources":["../../src/components/CommentPin.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentPropsWithoutRef, CSSProperties } from \"react\";\nimport { forwardRef } from \"react\";\n\nimport { cn } from \"../utils/cn\";\nimport { Avatar } from \"./internal/Avatar\";\n\nexport interface CommentPinProps extends ComponentPropsWithoutRef<\"button\"> {\n /**\n * The corner that points to the comment position.\n * Defaults to the bottom left corner.\n */\n corner?: \"top-left\" | \"top-right\" | \"bottom-right\" | \"bottom-left\";\n\n /**\n * The user ID to optionally display an avatar for.\n */\n userId?: string;\n\n /**\n * The size of the pin.\n */\n size?: string | number;\n}\n\n/**\n * Displays a comment pin that can be used as a trigger\n * for `FloatingComposer` and `FloatingThread`.\n */\nexport const CommentPin = forwardRef<HTMLButtonElement, CommentPinProps>(\n (\n {\n corner = \"bottom-left\",\n userId,\n size,\n type = \"button\",\n className,\n style,\n ...props\n },\n forwardedRef\n ) => {\n return (\n <button\n className={cn(\"lb-root lb-comment-pin\", className)}\n data-corner={corner}\n style={{ \"--lb-comment-pin-size\": size, ...style } as CSSProperties}\n type={type}\n {...props}\n ref={forwardedRef}\n >\n {userId ? (\n <Avatar className=\"lb-comment-pin-avatar\" userId={userId} />\n ) : null}\n </button>\n );\n }\n);\n"],"names":[],"mappings":";;;;;;;;;AA8BO;AAAmB;AAEtB;AACW;AACT;AACA;AACO;AACP;AACA;AACG;AAIL;AACE;AAAC;AAAA;AACkD;AACpC;AACoC;AACjD;AACI;AACC;AAID;AAAA;AACN;AAGN;;"}
1
+ {"version":3,"file":"CommentPin.cjs","sources":["../../src/components/CommentPin.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentPropsWithoutRef, CSSProperties, ReactNode } from \"react\";\nimport { forwardRef } from \"react\";\n\nimport { cn } from \"../utils/cn\";\nimport { px } from \"../utils/px\";\nimport { Avatar } from \"./internal/Avatar\";\n\nexport interface CommentPinProps extends ComponentPropsWithoutRef<\"button\"> {\n /**\n * The corner that points to the comment position.\n * Defaults to the bottom left corner.\n */\n corner?: \"top-left\" | \"top-right\" | \"bottom-right\" | \"bottom-left\";\n\n /**\n * The user ID to optionally display an avatar for.\n */\n userId?: string;\n\n /**\n * The size of the pin.\n */\n size?: string | number;\n\n /**\n * The content shown in the pin.\n * If provided, the `userId` prop is ignored.\n */\n children?: ReactNode;\n}\n\n/**\n * Displays a comment pin that can be used as a trigger\n * for `FloatingComposer` and `FloatingThread`.\n */\nexport const CommentPin = forwardRef<HTMLButtonElement, CommentPinProps>(\n (\n {\n corner = \"bottom-left\",\n userId,\n size,\n type = \"button\",\n className,\n style,\n children,\n ...props\n },\n forwardedRef\n ) => {\n return (\n <button\n className={cn(\"lb-root lb-comment-pin\", className)}\n data-corner={corner}\n style={{ \"--lb-comment-pin-size\": px(size), ...style } as CSSProperties}\n type={type}\n {...props}\n ref={forwardedRef}\n >\n {children ??\n (userId ? (\n <Avatar className=\"lb-comment-pin-avatar\" userId={userId} />\n ) : null)}\n </button>\n );\n }\n);\n"],"names":[],"mappings":";;;;;;;;;;AAqCO;AAAmB;AAEtB;AACW;AACT;AACA;AACO;AACP;AACA;AACA;AACG;AAIL;AACE;AAAC;AAAA;AACkD;AACpC;AACwC;AACrD;AACI;AACC;AAKC;AAAA;AACR;AAGN;;"}
@@ -2,6 +2,7 @@
2
2
  import { jsx } from 'react/jsx-runtime';
3
3
  import { forwardRef } from 'react';
4
4
  import { cn } from '../utils/cn.js';
5
+ import { px } from '../utils/px.js';
5
6
  import { Avatar } from './internal/Avatar.js';
6
7
 
7
8
 
@@ -13,6 +14,7 @@ const CommentPin = forwardRef(
13
14
  type = "button",
14
15
  className,
15
16
  style,
17
+ children,
16
18
  ...props
17
19
  }, forwardedRef) => {
18
20
  return /* @__PURE__ */ jsx(
@@ -20,11 +22,11 @@ const CommentPin = forwardRef(
20
22
  {
21
23
  className: cn("lb-root lb-comment-pin", className),
22
24
  "data-corner": corner,
23
- style: { "--lb-comment-pin-size": size, ...style },
25
+ style: { "--lb-comment-pin-size": px(size), ...style },
24
26
  type,
25
27
  ...props,
26
28
  ref: forwardedRef,
27
- children: userId ? /* @__PURE__ */ jsx(Avatar, { className: "lb-comment-pin-avatar", userId }) : null
29
+ children: children ?? (userId ? /* @__PURE__ */ jsx(Avatar, { className: "lb-comment-pin-avatar", userId }) : null)
28
30
  }
29
31
  );
30
32
  }
@@ -1 +1 @@
1
- {"version":3,"file":"CommentPin.js","sources":["../../src/components/CommentPin.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentPropsWithoutRef, CSSProperties } from \"react\";\nimport { forwardRef } from \"react\";\n\nimport { cn } from \"../utils/cn\";\nimport { Avatar } from \"./internal/Avatar\";\n\nexport interface CommentPinProps extends ComponentPropsWithoutRef<\"button\"> {\n /**\n * The corner that points to the comment position.\n * Defaults to the bottom left corner.\n */\n corner?: \"top-left\" | \"top-right\" | \"bottom-right\" | \"bottom-left\";\n\n /**\n * The user ID to optionally display an avatar for.\n */\n userId?: string;\n\n /**\n * The size of the pin.\n */\n size?: string | number;\n}\n\n/**\n * Displays a comment pin that can be used as a trigger\n * for `FloatingComposer` and `FloatingThread`.\n */\nexport const CommentPin = forwardRef<HTMLButtonElement, CommentPinProps>(\n (\n {\n corner = \"bottom-left\",\n userId,\n size,\n type = \"button\",\n className,\n style,\n ...props\n },\n forwardedRef\n ) => {\n return (\n <button\n className={cn(\"lb-root lb-comment-pin\", className)}\n data-corner={corner}\n style={{ \"--lb-comment-pin-size\": size, ...style } as CSSProperties}\n type={type}\n {...props}\n ref={forwardedRef}\n >\n {userId ? (\n <Avatar className=\"lb-comment-pin-avatar\" userId={userId} />\n ) : null}\n </button>\n );\n }\n);\n"],"names":[],"mappings":";;;;;;;AA8BO;AAAmB;AAEtB;AACW;AACT;AACA;AACO;AACP;AACA;AACG;AAIL;AACE;AAAC;AAAA;AACkD;AACpC;AACoC;AACjD;AACI;AACC;AAID;AAAA;AACN;AAGN;;"}
1
+ {"version":3,"file":"CommentPin.js","sources":["../../src/components/CommentPin.tsx"],"sourcesContent":["\"use client\";\n\nimport type { ComponentPropsWithoutRef, CSSProperties, ReactNode } from \"react\";\nimport { forwardRef } from \"react\";\n\nimport { cn } from \"../utils/cn\";\nimport { px } from \"../utils/px\";\nimport { Avatar } from \"./internal/Avatar\";\n\nexport interface CommentPinProps extends ComponentPropsWithoutRef<\"button\"> {\n /**\n * The corner that points to the comment position.\n * Defaults to the bottom left corner.\n */\n corner?: \"top-left\" | \"top-right\" | \"bottom-right\" | \"bottom-left\";\n\n /**\n * The user ID to optionally display an avatar for.\n */\n userId?: string;\n\n /**\n * The size of the pin.\n */\n size?: string | number;\n\n /**\n * The content shown in the pin.\n * If provided, the `userId` prop is ignored.\n */\n children?: ReactNode;\n}\n\n/**\n * Displays a comment pin that can be used as a trigger\n * for `FloatingComposer` and `FloatingThread`.\n */\nexport const CommentPin = forwardRef<HTMLButtonElement, CommentPinProps>(\n (\n {\n corner = \"bottom-left\",\n userId,\n size,\n type = \"button\",\n className,\n style,\n children,\n ...props\n },\n forwardedRef\n ) => {\n return (\n <button\n className={cn(\"lb-root lb-comment-pin\", className)}\n data-corner={corner}\n style={{ \"--lb-comment-pin-size\": px(size), ...style } as CSSProperties}\n type={type}\n {...props}\n ref={forwardedRef}\n >\n {children ??\n (userId ? (\n <Avatar className=\"lb-comment-pin-avatar\" userId={userId} />\n ) : null)}\n </button>\n );\n }\n);\n"],"names":[],"mappings":";;;;;;;;AAqCO;AAAmB;AAEtB;AACW;AACT;AACA;AACO;AACP;AACA;AACA;AACG;AAIL;AACE;AAAC;AAAA;AACkD;AACpC;AACwC;AACrD;AACI;AACC;AAKC;AAAA;AACR;AAGN;;"}
@@ -7,6 +7,7 @@ var _private = require('@liveblocks/react/_private');
7
7
  var react$1 = require('react');
8
8
  var animationLoop = require('../utils/animation-loop.cjs');
9
9
  var cn = require('../utils/cn.cjs');
10
+ var useRefs = require('../utils/use-refs.cjs');
10
11
  var useWindowFocus = require('../utils/use-window-focus.cjs');
11
12
  var Cursor = require('./Cursor.cjs');
12
13
 
@@ -156,15 +157,16 @@ function PresenceCursor({
156
157
  }
157
158
  const Cursors = react$1.forwardRef(
158
159
  ({ className, children, presenceKey = DEFAULT_PRESENCE_KEY, ...props }, forwardedRef) => {
159
- const ref = react$1.useRef(null);
160
+ const containerRef = react$1.useRef(null);
161
+ const mergedRefs = useRefs.useRefs(forwardedRef, containerRef);
160
162
  const updateMyPresence = react.useUpdateMyPresence();
161
163
  const othersConnectionIds = react.useOthersConnectionIds();
162
164
  const sizeRef = react$1.useRef(null);
163
165
  const [sizeEvents] = react$1.useState(() => core.makeEventSource());
164
166
  const isWindowFocused = useWindowFocus.useWindowFocus();
165
167
  react$1.useEffect(() => {
166
- const element = ref.current;
167
- if (!element) {
168
+ const container = containerRef.current;
169
+ if (!container) {
168
170
  return;
169
171
  }
170
172
  function setSize(size) {
@@ -173,7 +175,7 @@ const Cursors = react$1.forwardRef(
173
175
  }
174
176
  const observer = new ResizeObserver((entries) => {
175
177
  for (const entry of entries) {
176
- if (entry.target === element) {
178
+ if (entry.target === container) {
177
179
  setSize({
178
180
  width: entry.contentRect.width,
179
181
  height: entry.contentRect.height
@@ -182,21 +184,21 @@ const Cursors = react$1.forwardRef(
182
184
  }
183
185
  });
184
186
  setSize({
185
- width: element.clientWidth,
186
- height: element.clientHeight
187
+ width: container.clientWidth,
188
+ height: container.clientHeight
187
189
  });
188
- observer.observe(element);
190
+ observer.observe(container);
189
191
  return () => {
190
192
  observer.disconnect();
191
193
  };
192
194
  }, [sizeEvents]);
193
195
  const handlePointerMove = react$1.useCallback(
194
196
  (event) => {
195
- const element = ref.current;
196
- if (!element) {
197
+ const container = containerRef.current;
198
+ if (!container) {
197
199
  return;
198
200
  }
199
- const bounds = element.getBoundingClientRect();
201
+ const bounds = container.getBoundingClientRect();
200
202
  if (bounds.width === 0 || bounds.height === 0) {
201
203
  return;
202
204
  }
@@ -221,11 +223,6 @@ const Cursors = react$1.forwardRef(
221
223
  });
222
224
  }
223
225
  }, [isWindowFocused, updateMyPresence, presenceKey]);
224
- react$1.useImperativeHandle(
225
- forwardedRef,
226
- () => ref.current,
227
- []
228
- );
229
226
  return /* @__PURE__ */ jsxRuntime.jsxs(
230
227
  "div",
231
228
  {
@@ -233,7 +230,7 @@ const Cursors = react$1.forwardRef(
233
230
  ...props,
234
231
  onPointerMove: handlePointerMove,
235
232
  onPointerLeave: handlePointerLeave,
236
- ref,
233
+ ref: mergedRefs,
237
234
  children: [
238
235
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "lb-cursors-container", children: othersConnectionIds.map((connectionId) => /* @__PURE__ */ jsxRuntime.jsx(
239
236
  PresenceCursor,
@@ -1 +1 @@
1
- {"version":3,"file":"Cursors.cjs","sources":["../../src/components/Cursors.tsx"],"sourcesContent":["import type { EventSource } from \"@liveblocks/core\";\nimport { isPlainObject, makeEventSource } from \"@liveblocks/core\";\nimport {\n useOther,\n useOthersConnectionIds,\n useRoom,\n useUpdateMyPresence,\n useUser,\n} from \"@liveblocks/react\";\nimport { useLayoutEffect } from \"@liveblocks/react/_private\";\nimport type {\n ComponentPropsWithoutRef,\n MutableRefObject,\n PointerEvent,\n} from \"react\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from \"react\";\n\nimport { type Animatable, makeAnimationLoop } from \"../utils/animation-loop\";\nimport { cn } from \"../utils/cn\";\nimport { useWindowFocus } from \"../utils/use-window-focus\";\nimport { Cursor } from \"./Cursor\";\n\nconst STIFFNESS = 320;\nconst DAMPING = 32;\nconst EPSILON = 0.01;\n\nconst DEFAULT_PRESENCE_KEY = \"cursor\";\n\nexport interface CursorsProps extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * The key used to store the cursors in users' Presence.\n * This can be used to have multiple `Cursors` in a single room.\n */\n presenceKey?: string;\n}\n\ntype Coordinates = {\n x: number;\n y: number;\n};\n\ntype Size = {\n width: number;\n height: number;\n};\n\nfunction $string(value: unknown): string | undefined {\n return typeof value === \"string\" ? value : undefined;\n}\n\nfunction $coordinates(value: unknown): Coordinates | undefined {\n if (\n isPlainObject(value) &&\n typeof value.x === \"number\" &&\n typeof value.y === \"number\"\n ) {\n return value as Coordinates;\n }\n\n return undefined;\n}\n\n// Use a shared animation loop for all (active) springs.\nconst loop = makeAnimationLoop();\n\nfunction makeCoordinatesSpring() {\n const updates = makeEventSource<Coordinates | null>();\n let value: Coordinates | null = null;\n let target: Coordinates | null = null;\n const velocity = { x: 0, y: 0 };\n\n const spring: Animatable = {\n active: false,\n step(dt: number) {\n if (value === null || target === null) {\n spring.active = false;\n return;\n }\n\n const k = STIFFNESS;\n const d = DAMPING;\n const dx = value.x - target.x;\n const dy = value.y - target.y;\n\n velocity.x += (-k * dx - d * velocity.x) * dt;\n velocity.y += (-k * dy - d * velocity.y) * dt;\n\n const nx = value.x + velocity.x * dt;\n const ny = value.y + velocity.y * dt;\n\n if (nx !== value.x || ny !== value.y) {\n value.x = nx;\n value.y = ny;\n updates.notify(value);\n }\n\n if (\n Math.abs(velocity.x) < EPSILON &&\n Math.abs(velocity.y) < EPSILON &&\n Math.abs(target.x - value.x) < EPSILON &&\n Math.abs(target.y - value.y) < EPSILON\n ) {\n if (value.x !== target.x || value.y !== target.y) {\n value.x = target.x;\n value.y = target.y;\n updates.notify(value);\n }\n\n velocity.x = 0;\n velocity.y = 0;\n spring.active = false;\n }\n },\n };\n\n return {\n get() {\n return value;\n },\n set(point: Coordinates | null) {\n if (point === null) {\n value = null;\n target = null;\n velocity.x = 0;\n velocity.y = 0;\n spring.active = false;\n loop.remove(spring);\n updates.notify(null);\n return;\n }\n\n if (value === null) {\n value = { x: point.x, y: point.y };\n target = point;\n velocity.x = 0;\n velocity.y = 0;\n updates.notify(value);\n return;\n }\n\n target = point;\n\n if (!spring.active && (value.x !== target.x || value.y !== target.y)) {\n spring.active = true;\n loop.add(spring);\n }\n },\n subscribe: updates.subscribe,\n dispose() {\n spring.active = false;\n loop.remove(spring);\n updates.dispose();\n },\n };\n}\n\nfunction PresenceCursor({\n connectionId,\n presenceKey,\n sizeRef,\n sizeEvents,\n}: {\n connectionId: number;\n presenceKey: string;\n sizeRef: MutableRefObject<Size | null>;\n sizeEvents: EventSource<void>;\n}) {\n const room = useRoom();\n const cursorRef = useRef<HTMLDivElement>(null);\n const userId = useOther(connectionId, (other) => $string(other.id));\n const { user, isLoading } = useUser(userId ?? \"\");\n const hasUserInfo = userId !== undefined && !isLoading;\n const color = $string(user?.color);\n const name = $string(user?.name);\n\n useLayoutEffect(() => {\n const spring = makeCoordinatesSpring();\n\n function update() {\n const element = cursorRef.current;\n const coordinates = spring.get();\n\n if (!element) {\n return;\n }\n\n if (!hasUserInfo || coordinates === null) {\n element.style.transform = \"translate3d(0, 0, 0)\";\n element.style.display = \"none\";\n return;\n }\n\n if (sizeRef.current) {\n element.style.transform = `translate3d(${coordinates.x * sizeRef.current.width}px, ${coordinates.y * sizeRef.current.height}px, 0)`;\n }\n\n element.style.display = \"\";\n }\n\n const unsubscribeSpring = spring.subscribe(update);\n const unsubscribeSize = sizeEvents.subscribe(update);\n update();\n\n const unsubscribeOther = room.events.others.subscribe(({ others }) => {\n const other = others.find((other) => other.connectionId === connectionId);\n const cursor = $coordinates(other?.presence[presenceKey]);\n\n spring.set(cursor ?? null);\n });\n\n return () => {\n spring.dispose();\n unsubscribeSpring();\n unsubscribeSize();\n unsubscribeOther();\n };\n }, [room, connectionId, presenceKey, sizeRef, sizeEvents, hasUserInfo]);\n\n return (\n <Cursor\n color={color}\n label={name}\n ref={cursorRef}\n style={{ display: \"none\" }}\n />\n );\n}\n\n/**\n * Displays multiplayer cursors.\n */\nexport const Cursors = forwardRef<HTMLDivElement, CursorsProps>(\n (\n { className, children, presenceKey = DEFAULT_PRESENCE_KEY, ...props },\n forwardedRef\n ) => {\n const ref = useRef<HTMLDivElement>(null);\n const updateMyPresence = useUpdateMyPresence();\n const othersConnectionIds = useOthersConnectionIds();\n const sizeRef = useRef<Size | null>(null);\n const [sizeEvents] = useState(() => makeEventSource<void>());\n const isWindowFocused = useWindowFocus();\n\n useEffect(() => {\n const element = ref.current;\n\n if (!element) {\n return;\n }\n\n function setSize(size: Size) {\n sizeRef.current = size;\n sizeEvents.notify();\n }\n\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n if (entry.target === element) {\n setSize({\n width: entry.contentRect.width,\n height: entry.contentRect.height,\n });\n }\n }\n });\n\n setSize({\n width: element.clientWidth,\n height: element.clientHeight,\n });\n\n observer.observe(element);\n\n return () => {\n observer.disconnect();\n };\n }, [sizeEvents]);\n\n const handlePointerMove = useCallback(\n (event: PointerEvent) => {\n const element = ref.current;\n\n if (!element) {\n return;\n }\n\n const bounds = element.getBoundingClientRect();\n\n if (bounds.width === 0 || bounds.height === 0) {\n return;\n }\n\n updateMyPresence({\n [presenceKey]: {\n x: (event.clientX - bounds.left) / bounds.width,\n y: (event.clientY - bounds.top) / bounds.height,\n },\n });\n },\n [updateMyPresence, presenceKey]\n );\n\n const handlePointerLeave = useCallback(() => {\n updateMyPresence({\n [presenceKey]: null,\n });\n }, [updateMyPresence, presenceKey]);\n\n useEffect(() => {\n if (!isWindowFocused) {\n updateMyPresence({\n [presenceKey]: null,\n });\n }\n }, [isWindowFocused, updateMyPresence, presenceKey]);\n\n useImperativeHandle<HTMLDivElement | null, HTMLDivElement | null>(\n forwardedRef,\n () => ref.current,\n []\n );\n\n return (\n <div\n className={cn(\"lb-root lb-cursors\", className)}\n {...props}\n onPointerMove={handlePointerMove}\n onPointerLeave={handlePointerLeave}\n ref={ref}\n >\n <div className=\"lb-cursors-container\">\n {othersConnectionIds.map((connectionId) => (\n <PresenceCursor\n key={connectionId}\n connectionId={connectionId}\n presenceKey={presenceKey}\n sizeRef={sizeRef}\n sizeEvents={sizeEvents}\n />\n ))}\n </div>\n\n {children}\n </div>\n );\n }\n);\n"],"names":["isPlainObject","makeAnimationLoop","makeEventSource","useRoom","useRef","useOther","useUser","useLayoutEffect","other","jsx","Cursor","forwardRef","useUpdateMyPresence","useOthersConnectionIds","useState","useWindowFocus","useEffect","useCallback","useImperativeHandle","jsxs","cn"],"mappings":"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"}
1
+ {"version":3,"file":"Cursors.cjs","sources":["../../src/components/Cursors.tsx"],"sourcesContent":["import type { EventSource } from \"@liveblocks/core\";\nimport { isPlainObject, makeEventSource } from \"@liveblocks/core\";\nimport {\n useOther,\n useOthersConnectionIds,\n useRoom,\n useUpdateMyPresence,\n useUser,\n} from \"@liveblocks/react\";\nimport { useLayoutEffect } from \"@liveblocks/react/_private\";\nimport type {\n ComponentPropsWithoutRef,\n MutableRefObject,\n PointerEvent,\n} from \"react\";\nimport { forwardRef, useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { type Animatable, makeAnimationLoop } from \"../utils/animation-loop\";\nimport { cn } from \"../utils/cn\";\nimport { useRefs } from \"../utils/use-refs\";\nimport { useWindowFocus } from \"../utils/use-window-focus\";\nimport { Cursor } from \"./Cursor\";\n\nconst STIFFNESS = 320;\nconst DAMPING = 32;\nconst EPSILON = 0.01;\n\nconst DEFAULT_PRESENCE_KEY = \"cursor\";\n\nexport interface CursorsProps extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * The key used to store the cursors in users' Presence.\n * This can be used to have multiple `Cursors` in a single room.\n */\n presenceKey?: string;\n}\n\ntype Coordinates = {\n x: number;\n y: number;\n};\n\ntype Size = {\n width: number;\n height: number;\n};\n\nfunction $string(value: unknown): string | undefined {\n return typeof value === \"string\" ? value : undefined;\n}\n\nfunction $coordinates(value: unknown): Coordinates | undefined {\n if (\n isPlainObject(value) &&\n typeof value.x === \"number\" &&\n typeof value.y === \"number\"\n ) {\n return value as Coordinates;\n }\n\n return undefined;\n}\n\n// Use a shared animation loop for all (active) springs.\nconst loop = makeAnimationLoop();\n\nfunction makeCoordinatesSpring() {\n const updates = makeEventSource<Coordinates | null>();\n let value: Coordinates | null = null;\n let target: Coordinates | null = null;\n const velocity = { x: 0, y: 0 };\n\n const spring: Animatable = {\n active: false,\n step(dt: number) {\n if (value === null || target === null) {\n spring.active = false;\n return;\n }\n\n const k = STIFFNESS;\n const d = DAMPING;\n const dx = value.x - target.x;\n const dy = value.y - target.y;\n\n velocity.x += (-k * dx - d * velocity.x) * dt;\n velocity.y += (-k * dy - d * velocity.y) * dt;\n\n const nx = value.x + velocity.x * dt;\n const ny = value.y + velocity.y * dt;\n\n if (nx !== value.x || ny !== value.y) {\n value.x = nx;\n value.y = ny;\n updates.notify(value);\n }\n\n if (\n Math.abs(velocity.x) < EPSILON &&\n Math.abs(velocity.y) < EPSILON &&\n Math.abs(target.x - value.x) < EPSILON &&\n Math.abs(target.y - value.y) < EPSILON\n ) {\n if (value.x !== target.x || value.y !== target.y) {\n value.x = target.x;\n value.y = target.y;\n updates.notify(value);\n }\n\n velocity.x = 0;\n velocity.y = 0;\n spring.active = false;\n }\n },\n };\n\n return {\n get() {\n return value;\n },\n set(point: Coordinates | null) {\n if (point === null) {\n value = null;\n target = null;\n velocity.x = 0;\n velocity.y = 0;\n spring.active = false;\n loop.remove(spring);\n updates.notify(null);\n return;\n }\n\n if (value === null) {\n value = { x: point.x, y: point.y };\n target = point;\n velocity.x = 0;\n velocity.y = 0;\n updates.notify(value);\n return;\n }\n\n target = point;\n\n if (!spring.active && (value.x !== target.x || value.y !== target.y)) {\n spring.active = true;\n loop.add(spring);\n }\n },\n subscribe: updates.subscribe,\n dispose() {\n spring.active = false;\n loop.remove(spring);\n updates.dispose();\n },\n };\n}\n\nfunction PresenceCursor({\n connectionId,\n presenceKey,\n sizeRef,\n sizeEvents,\n}: {\n connectionId: number;\n presenceKey: string;\n sizeRef: MutableRefObject<Size | null>;\n sizeEvents: EventSource<void>;\n}) {\n const room = useRoom();\n const cursorRef = useRef<HTMLDivElement>(null);\n const userId = useOther(connectionId, (other) => $string(other.id));\n const { user, isLoading } = useUser(userId ?? \"\");\n const hasUserInfo = userId !== undefined && !isLoading;\n const color = $string(user?.color);\n const name = $string(user?.name);\n\n useLayoutEffect(() => {\n const spring = makeCoordinatesSpring();\n\n function update() {\n const element = cursorRef.current;\n const coordinates = spring.get();\n\n if (!element) {\n return;\n }\n\n if (!hasUserInfo || coordinates === null) {\n element.style.transform = \"translate3d(0, 0, 0)\";\n element.style.display = \"none\";\n return;\n }\n\n if (sizeRef.current) {\n element.style.transform = `translate3d(${coordinates.x * sizeRef.current.width}px, ${coordinates.y * sizeRef.current.height}px, 0)`;\n }\n\n element.style.display = \"\";\n }\n\n const unsubscribeSpring = spring.subscribe(update);\n const unsubscribeSize = sizeEvents.subscribe(update);\n update();\n\n const unsubscribeOther = room.events.others.subscribe(({ others }) => {\n const other = others.find((other) => other.connectionId === connectionId);\n const cursor = $coordinates(other?.presence[presenceKey]);\n\n spring.set(cursor ?? null);\n });\n\n return () => {\n spring.dispose();\n unsubscribeSpring();\n unsubscribeSize();\n unsubscribeOther();\n };\n }, [room, connectionId, presenceKey, sizeRef, sizeEvents, hasUserInfo]);\n\n return (\n <Cursor\n color={color}\n label={name}\n ref={cursorRef}\n style={{ display: \"none\" }}\n />\n );\n}\n\n/**\n * Displays multiplayer cursors.\n */\nexport const Cursors = forwardRef<HTMLDivElement, CursorsProps>(\n (\n { className, children, presenceKey = DEFAULT_PRESENCE_KEY, ...props },\n forwardedRef\n ) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const mergedRefs = useRefs(forwardedRef, containerRef);\n const updateMyPresence = useUpdateMyPresence();\n const othersConnectionIds = useOthersConnectionIds();\n const sizeRef = useRef<Size | null>(null);\n const [sizeEvents] = useState(() => makeEventSource<void>());\n const isWindowFocused = useWindowFocus();\n\n useEffect(() => {\n const container = containerRef.current;\n\n if (!container) {\n return;\n }\n\n function setSize(size: Size) {\n sizeRef.current = size;\n sizeEvents.notify();\n }\n\n const observer = new ResizeObserver((entries) => {\n for (const entry of entries) {\n if (entry.target === container) {\n setSize({\n width: entry.contentRect.width,\n height: entry.contentRect.height,\n });\n }\n }\n });\n\n setSize({\n width: container.clientWidth,\n height: container.clientHeight,\n });\n\n observer.observe(container);\n\n return () => {\n observer.disconnect();\n };\n }, [sizeEvents]);\n\n const handlePointerMove = useCallback(\n (event: PointerEvent) => {\n const container = containerRef.current;\n\n if (!container) {\n return;\n }\n\n const bounds = container.getBoundingClientRect();\n\n if (bounds.width === 0 || bounds.height === 0) {\n return;\n }\n\n updateMyPresence({\n [presenceKey]: {\n x: (event.clientX - bounds.left) / bounds.width,\n y: (event.clientY - bounds.top) / bounds.height,\n },\n });\n },\n [updateMyPresence, presenceKey]\n );\n\n const handlePointerLeave = useCallback(() => {\n updateMyPresence({\n [presenceKey]: null,\n });\n }, [updateMyPresence, presenceKey]);\n\n useEffect(() => {\n if (!isWindowFocused) {\n updateMyPresence({\n [presenceKey]: null,\n });\n }\n }, [isWindowFocused, updateMyPresence, presenceKey]);\n\n return (\n <div\n className={cn(\"lb-root lb-cursors\", className)}\n {...props}\n onPointerMove={handlePointerMove}\n onPointerLeave={handlePointerLeave}\n ref={mergedRefs}\n >\n <div className=\"lb-cursors-container\">\n {othersConnectionIds.map((connectionId) => (\n <PresenceCursor\n key={connectionId}\n connectionId={connectionId}\n presenceKey={presenceKey}\n sizeRef={sizeRef}\n sizeEvents={sizeEvents}\n />\n ))}\n </div>\n\n {children}\n </div>\n );\n }\n);\n"],"names":["isPlainObject","makeAnimationLoop","makeEventSource","useRoom","useRef","useOther","useUser","useLayoutEffect","other","jsx","Cursor","forwardRef","useRefs","useUpdateMyPresence","useOthersConnectionIds","useState","useWindowFocus","useEffect","useCallback","jsxs","cn"],"mappings":"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"}
@@ -2,9 +2,10 @@ import { jsx, jsxs } from 'react/jsx-runtime';
2
2
  import { isPlainObject, makeEventSource } from '@liveblocks/core';
3
3
  import { useRoom, useOther, useUser, useUpdateMyPresence, useOthersConnectionIds } from '@liveblocks/react';
4
4
  import { useLayoutEffect } from '@liveblocks/react/_private';
5
- import { useRef, forwardRef, useState, useEffect, useCallback, useImperativeHandle } from 'react';
5
+ import { useRef, forwardRef, useState, useEffect, useCallback } from 'react';
6
6
  import { makeAnimationLoop } from '../utils/animation-loop.js';
7
7
  import { cn } from '../utils/cn.js';
8
+ import { useRefs } from '../utils/use-refs.js';
8
9
  import { useWindowFocus } from '../utils/use-window-focus.js';
9
10
  import { Cursor } from './Cursor.js';
10
11
 
@@ -154,15 +155,16 @@ function PresenceCursor({
154
155
  }
155
156
  const Cursors = forwardRef(
156
157
  ({ className, children, presenceKey = DEFAULT_PRESENCE_KEY, ...props }, forwardedRef) => {
157
- const ref = useRef(null);
158
+ const containerRef = useRef(null);
159
+ const mergedRefs = useRefs(forwardedRef, containerRef);
158
160
  const updateMyPresence = useUpdateMyPresence();
159
161
  const othersConnectionIds = useOthersConnectionIds();
160
162
  const sizeRef = useRef(null);
161
163
  const [sizeEvents] = useState(() => makeEventSource());
162
164
  const isWindowFocused = useWindowFocus();
163
165
  useEffect(() => {
164
- const element = ref.current;
165
- if (!element) {
166
+ const container = containerRef.current;
167
+ if (!container) {
166
168
  return;
167
169
  }
168
170
  function setSize(size) {
@@ -171,7 +173,7 @@ const Cursors = forwardRef(
171
173
  }
172
174
  const observer = new ResizeObserver((entries) => {
173
175
  for (const entry of entries) {
174
- if (entry.target === element) {
176
+ if (entry.target === container) {
175
177
  setSize({
176
178
  width: entry.contentRect.width,
177
179
  height: entry.contentRect.height
@@ -180,21 +182,21 @@ const Cursors = forwardRef(
180
182
  }
181
183
  });
182
184
  setSize({
183
- width: element.clientWidth,
184
- height: element.clientHeight
185
+ width: container.clientWidth,
186
+ height: container.clientHeight
185
187
  });
186
- observer.observe(element);
188
+ observer.observe(container);
187
189
  return () => {
188
190
  observer.disconnect();
189
191
  };
190
192
  }, [sizeEvents]);
191
193
  const handlePointerMove = useCallback(
192
194
  (event) => {
193
- const element = ref.current;
194
- if (!element) {
195
+ const container = containerRef.current;
196
+ if (!container) {
195
197
  return;
196
198
  }
197
- const bounds = element.getBoundingClientRect();
199
+ const bounds = container.getBoundingClientRect();
198
200
  if (bounds.width === 0 || bounds.height === 0) {
199
201
  return;
200
202
  }
@@ -219,11 +221,6 @@ const Cursors = forwardRef(
219
221
  });
220
222
  }
221
223
  }, [isWindowFocused, updateMyPresence, presenceKey]);
222
- useImperativeHandle(
223
- forwardedRef,
224
- () => ref.current,
225
- []
226
- );
227
224
  return /* @__PURE__ */ jsxs(
228
225
  "div",
229
226
  {
@@ -231,7 +228,7 @@ const Cursors = forwardRef(
231
228
  ...props,
232
229
  onPointerMove: handlePointerMove,
233
230
  onPointerLeave: handlePointerLeave,
234
- ref,
231
+ ref: mergedRefs,
235
232
  children: [
236
233
  /* @__PURE__ */ jsx("div", { className: "lb-cursors-container", children: othersConnectionIds.map((connectionId) => /* @__PURE__ */ jsx(
237
234
  PresenceCursor,