@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.
- package/dist/_private/index.cjs +2 -0
- package/dist/_private/index.cjs.map +1 -1
- package/dist/_private/index.d.cts +3 -1
- package/dist/_private/index.d.ts +3 -1
- package/dist/_private/index.js +1 -0
- package/dist/_private/index.js.map +1 -1
- package/dist/components/AvatarStack.cjs +15 -10
- package/dist/components/AvatarStack.cjs.map +1 -1
- package/dist/components/AvatarStack.js +15 -10
- package/dist/components/AvatarStack.js.map +1 -1
- package/dist/components/CommentPin.cjs +4 -2
- package/dist/components/CommentPin.cjs.map +1 -1
- package/dist/components/CommentPin.js +4 -2
- package/dist/components/CommentPin.js.map +1 -1
- package/dist/components/Cursors.cjs +13 -16
- package/dist/components/Cursors.cjs.map +1 -1
- package/dist/components/Cursors.js +14 -17
- package/dist/components/Cursors.js.map +1 -1
- package/dist/components/FloatingComposer.cjs +14 -2
- package/dist/components/FloatingComposer.cjs.map +1 -1
- package/dist/components/FloatingComposer.js +15 -3
- package/dist/components/FloatingComposer.js.map +1 -1
- package/dist/components/FloatingThread.cjs +1 -0
- package/dist/components/FloatingThread.cjs.map +1 -1
- package/dist/components/FloatingThread.js +1 -0
- package/dist/components/FloatingThread.js.map +1 -1
- package/dist/components/internal/Dropdown.cjs +1 -0
- package/dist/components/internal/Dropdown.cjs.map +1 -1
- package/dist/components/internal/Dropdown.js +1 -0
- package/dist/components/internal/Dropdown.js.map +1 -1
- package/dist/components/internal/EmojiPicker.cjs +1 -0
- package/dist/components/internal/EmojiPicker.cjs.map +1 -1
- package/dist/components/internal/EmojiPicker.js +1 -0
- package/dist/components/internal/EmojiPicker.js.map +1 -1
- package/dist/components/internal/Tooltip.cjs +1 -0
- package/dist/components/internal/Tooltip.cjs.map +1 -1
- package/dist/components/internal/Tooltip.js +1 -0
- package/dist/components/internal/Tooltip.js.map +1 -1
- package/dist/icon.cjs +2 -0
- package/dist/icon.cjs.map +1 -1
- package/dist/icon.js +1 -0
- package/dist/icon.js.map +1 -1
- package/dist/icons/Plus.cjs +11 -0
- package/dist/icons/Plus.cjs.map +1 -0
- package/dist/icons/Plus.js +9 -0
- package/dist/icons/Plus.js.map +1 -0
- package/dist/icons/index.cjs +2 -0
- package/dist/icons/index.cjs.map +1 -1
- package/dist/icons/index.js +1 -0
- package/dist/icons/index.js.map +1 -1
- package/dist/index.d.cts +11 -3
- package/dist/index.d.ts +11 -3
- package/dist/utils/px.cjs +14 -0
- package/dist/utils/px.cjs.map +1 -0
- package/dist/utils/px.js +12 -0
- package/dist/utils/px.js.map +1 -0
- package/dist/version.cjs +1 -1
- package/dist/version.cjs.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +4 -4
- package/src/styles/index.css +8 -1
- package/styles.css +1 -1
- package/styles.css.map +1 -1
package/dist/_private/index.cjs
CHANGED
|
@@ -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 };
|
package/dist/_private/index.d.ts
CHANGED
|
@@ -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 };
|
package/dist/_private/index.js
CHANGED
|
@@ -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(
|
|
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 =
|
|
30
|
-
selfId,
|
|
31
|
-
|
|
32
|
-
|
|
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
|
|
37
|
-
const
|
|
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
|
|
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
|
|
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(
|
|
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 =
|
|
28
|
-
selfId,
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
35
|
-
const
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
167
|
-
if (!
|
|
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 ===
|
|
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:
|
|
186
|
-
height:
|
|
187
|
+
width: container.clientWidth,
|
|
188
|
+
height: container.clientHeight
|
|
187
189
|
});
|
|
188
|
-
observer.observe(
|
|
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
|
|
196
|
-
if (!
|
|
197
|
+
const container = containerRef.current;
|
|
198
|
+
if (!container) {
|
|
197
199
|
return;
|
|
198
200
|
}
|
|
199
|
-
const bounds =
|
|
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
|
|
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
|
|
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
|
|
165
|
-
if (!
|
|
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 ===
|
|
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:
|
|
184
|
-
height:
|
|
185
|
+
width: container.clientWidth,
|
|
186
|
+
height: container.clientHeight
|
|
185
187
|
});
|
|
186
|
-
observer.observe(
|
|
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
|
|
194
|
-
if (!
|
|
195
|
+
const container = containerRef.current;
|
|
196
|
+
if (!container) {
|
|
195
197
|
return;
|
|
196
198
|
}
|
|
197
|
-
const bounds =
|
|
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,
|