@tldraw/editor
Version:
tldraw infinite canvas SDK (editor).
142 lines (141 loc) • 5.04 kB
JavaScript
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
import { track } from "@tldraw/state-react";
import { useEffect, useRef, useState } from "react";
import { useEditor } from "../hooks/useEditor.mjs";
import { useEditorComponents } from "../hooks/useEditorComponents.mjs";
import { usePeerIds } from "../hooks/usePeerIds.mjs";
import { usePresence } from "../hooks/usePresence.mjs";
const LiveCollaborators = track(function Collaborators() {
const peerIds = usePeerIds();
return peerIds.map((id) => /* @__PURE__ */ jsx(CollaboratorGuard, { collaboratorId: id }, id));
});
const CollaboratorGuard = track(function CollaboratorGuard2({
collaboratorId
}) {
const editor = useEditor();
const presence = usePresence(collaboratorId);
const collaboratorState = useCollaboratorState(editor, presence);
if (!(presence && presence.currentPageId === editor.getCurrentPageId())) {
return null;
}
switch (collaboratorState) {
case "inactive": {
const { followingUserId, highlightedUserIds } = editor.getInstanceState();
if (!(followingUserId === presence.userId || highlightedUserIds.includes(presence.userId))) {
return null;
}
break;
}
case "idle": {
const { highlightedUserIds } = editor.getInstanceState();
if (presence.followingUserId === editor.user.getId() && !(presence.chatMessage || highlightedUserIds.includes(presence.userId))) {
return null;
}
break;
}
case "active": {
break;
}
}
return /* @__PURE__ */ jsx(Collaborator, { latestPresence: presence });
});
const Collaborator = track(function Collaborator2({
latestPresence
}) {
const editor = useEditor();
const {
CollaboratorBrush,
CollaboratorScribble,
CollaboratorCursor,
CollaboratorHint,
CollaboratorShapeIndicator
} = useEditorComponents();
const zoomLevel = editor.getZoomLevel();
const viewportPageBounds = editor.getViewportPageBounds();
const { userId, chatMessage, brush, scribbles, selectedShapeIds, userName, cursor, color } = latestPresence;
if (!cursor) return null;
const isCursorInViewport = !(cursor.x < viewportPageBounds.minX - 12 / zoomLevel || cursor.y < viewportPageBounds.minY - 16 / zoomLevel || cursor.x > viewportPageBounds.maxX - 12 / zoomLevel || cursor.y > viewportPageBounds.maxY - 16 / zoomLevel);
return /* @__PURE__ */ jsxs(Fragment, { children: [
brush && CollaboratorBrush ? /* @__PURE__ */ jsx(
CollaboratorBrush,
{
className: "tl-collaborator__brush",
userId,
brush,
color,
opacity: 0.1
},
userId + "_brush"
) : null,
isCursorInViewport && CollaboratorCursor ? /* @__PURE__ */ jsx(
CollaboratorCursor,
{
className: "tl-collaborator__cursor",
userId,
point: cursor,
color,
zoom: zoomLevel,
name: userName !== "New User" ? userName : null,
chatMessage: chatMessage ?? ""
},
userId + "_cursor"
) : CollaboratorHint ? /* @__PURE__ */ jsx(
CollaboratorHint,
{
className: "tl-collaborator__cursor-hint",
userId,
point: cursor,
color,
zoom: zoomLevel,
viewport: viewportPageBounds
},
userId + "_cursor_hint"
) : null,
CollaboratorScribble && scribbles.length ? /* @__PURE__ */ jsx(Fragment, { children: scribbles.map((scribble) => /* @__PURE__ */ jsx(
CollaboratorScribble,
{
className: "tl-collaborator__scribble",
userId,
scribble,
color,
zoom: zoomLevel,
opacity: scribble.color === "laser" ? 0.5 : 0.1
},
userId + "_scribble_" + scribble.id
)) }) : null,
CollaboratorShapeIndicator && selectedShapeIds.filter((id) => !editor.isShapeHidden(id)).map((shapeId) => /* @__PURE__ */ jsx(
CollaboratorShapeIndicator,
{
className: "tl-collaborator__shape-indicator",
userId,
shapeId,
color,
opacity: 0.5
},
userId + "_" + shapeId
))
] });
});
function getStateFromElapsedTime(editor, elapsed) {
return elapsed > editor.options.collaboratorInactiveTimeoutMs ? "inactive" : elapsed > editor.options.collaboratorIdleTimeoutMs ? "idle" : "active";
}
function useCollaboratorState(editor, latestPresence) {
const rLastActivityTimestamp = useRef(latestPresence?.lastActivityTimestamp ?? -1);
const [state, setState] = useState(
() => getStateFromElapsedTime(editor, Date.now() - rLastActivityTimestamp.current)
);
useEffect(() => {
const interval = editor.timers.setInterval(() => {
setState(getStateFromElapsedTime(editor, Date.now() - rLastActivityTimestamp.current));
}, editor.options.collaboratorCheckIntervalMs);
return () => clearInterval(interval);
}, [editor]);
if (latestPresence) {
rLastActivityTimestamp.current = latestPresence.lastActivityTimestamp ?? Infinity;
}
return state;
}
export {
LiveCollaborators
};
//# sourceMappingURL=LiveCollaborators.mjs.map