From 6ec259a0e71174651bae95d4628138bf6fd68742 Mon Sep 17 00:00:00 2001
From: kj_sh604
Date: Sun, 15 Mar 2026 16:19:35 -0400
Subject: refactor: packages/

---
 packages/excalidraw/actions/actionNavigate.tsx | 133 +++++++++++++++++++++++++
 1 file changed, 133 insertions(+)
 create mode 100644 packages/excalidraw/actions/actionNavigate.tsx

(limited to 'packages/excalidraw/actions/actionNavigate.tsx')

diff --git a/packages/excalidraw/actions/actionNavigate.tsx b/packages/excalidraw/actions/actionNavigate.tsx
new file mode 100644
index 0000000..4803720
--- /dev/null
+++ b/packages/excalidraw/actions/actionNavigate.tsx
@@ -0,0 +1,133 @@
+import { getClientColor } from "../clients";
+import { Avatar } from "../components/Avatar";
+import type { GoToCollaboratorComponentProps } from "../components/UserList";
+import {
+  eyeIcon,
+  microphoneIcon,
+  microphoneMutedIcon,
+} from "../components/icons";
+import { t } from "../i18n";
+import { CaptureUpdateAction } from "../store";
+import type { Collaborator } from "../types";
+import { register } from "./register";
+import clsx from "clsx";
+
+export const actionGoToCollaborator = register({
+  name: "goToCollaborator",
+  label: "Go to a collaborator",
+  viewMode: true,
+  trackEvent: { category: "collab" },
+  perform: (_elements, appState, collaborator: Collaborator) => {
+    if (
+      !collaborator.socketId ||
+      appState.userToFollow?.socketId === collaborator.socketId ||
+      collaborator.isCurrentUser
+    ) {
+      return {
+        appState: {
+          ...appState,
+          userToFollow: null,
+        },
+        captureUpdate: CaptureUpdateAction.EVENTUALLY,
+      };
+    }
+
+    return {
+      appState: {
+        ...appState,
+        userToFollow: {
+          socketId: collaborator.socketId,
+          username: collaborator.username || "",
+        },
+        // Close mobile menu
+        openMenu: appState.openMenu === "canvas" ? null : appState.openMenu,
+      },
+      captureUpdate: CaptureUpdateAction.EVENTUALLY,
+    };
+  },
+  PanelComponent: ({ updateData, data, appState }) => {
+    const { socketId, collaborator, withName, isBeingFollowed } =
+      data as GoToCollaboratorComponentProps;
+
+    const background = getClientColor(socketId, collaborator);
+
+    const statusClassNames = clsx({
+      "is-followed": isBeingFollowed,
+      "is-current-user": collaborator.isCurrentUser === true,
+      "is-speaking": collaborator.isSpeaking,
+      "is-in-call": collaborator.isInCall,
+      "is-muted": collaborator.isMuted,
+    });
+
+    const statusIconJSX = collaborator.isInCall ? (
+      collaborator.isSpeaking ? (
+        <div
+          className="UserList__collaborator-status-icon-speaking-indicator"
+          title={t("userList.hint.isSpeaking")}
+        >
+          <div />
+          <div />
+          <div />
+        </div>
+      ) : collaborator.isMuted ? (
+        <div
+          className="UserList__collaborator-status-icon-microphone-muted"
+          title={t("userList.hint.micMuted")}
+        >
+          {microphoneMutedIcon}
+        </div>
+      ) : (
+        <div title={t("userList.hint.inCall")}>{microphoneIcon}</div>
+      )
+    ) : null;
+
+    return withName ? (
+      <div
+        className={`dropdown-menu-item dropdown-menu-item-base UserList__collaborator ${statusClassNames}`}
+        style={{ [`--avatar-size` as any]: "1.5rem" }}
+        onClick={() => updateData<Collaborator>(collaborator)}
+      >
+        <Avatar
+          color={background}
+          onClick={() => {}}
+          name={collaborator.username || ""}
+          src={collaborator.avatarUrl}
+          className={statusClassNames}
+        />
+        <div className="UserList__collaborator-name">
+          {collaborator.username}
+        </div>
+        <div className="UserList__collaborator-status-icons" aria-hidden>
+          {isBeingFollowed && (
+            <div
+              className="UserList__collaborator-status-icon-is-followed"
+              title={t("userList.hint.followStatus")}
+            >
+              {eyeIcon}
+            </div>
+          )}
+          {statusIconJSX}
+        </div>
+      </div>
+    ) : (
+      <div
+        className={`UserList__collaborator UserList__collaborator--avatar-only ${statusClassNames}`}
+      >
+        <Avatar
+          color={background}
+          onClick={() => {
+            updateData(collaborator);
+          }}
+          name={collaborator.username || ""}
+          src={collaborator.avatarUrl}
+          className={statusClassNames}
+        />
+        {statusIconJSX && (
+          <div className="UserList__collaborator-status-icon">
+            {statusIconJSX}
+          </div>
+        )}
+      </div>
+    );
+  },
+});
-- 
cgit v1.2.3

