@rencar-dev/feature-modules-public 1.2.2 → 1.2.3

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.
@@ -22,6 +22,7 @@ var presence_exports = {};
22
22
  __export(presence_exports, {
23
23
  PresenceAvatars: () => PresenceAvatars,
24
24
  PresenceFloating: () => PresenceFloating,
25
+ PresenceProvider: () => PresenceProvider,
25
26
  disconnectPresenceSocket: () => disconnectPresenceSocket,
26
27
  getPresenceSocket: () => getPresenceSocket,
27
28
  initPresenceSocket: () => initPresenceSocket,
@@ -29,6 +30,7 @@ __export(presence_exports, {
29
30
  usePresence: () => usePresence,
30
31
  usePresenceAvatarsState: () => usePresenceAvatarsState,
31
32
  usePresenceFloatingState: () => usePresenceFloatingState,
33
+ usePresenceStatus: () => usePresenceStatus,
32
34
  usePresenceWatch: () => usePresenceWatch
33
35
  });
34
36
  module.exports = __toCommonJS(presence_exports);
@@ -97,18 +99,29 @@ function usePresence(options) {
97
99
  setUsers([]);
98
100
  }
99
101
  }, [enabled]);
102
+ const currentUserKey = JSON.stringify(currentUser);
100
103
  (0, import_react.useEffect)(() => {
101
104
  if (!enabled) {
102
105
  return;
103
106
  }
107
+ if (!isPresenceSocketInitialized()) {
108
+ console.warn(
109
+ "[presence] Socket not initialized. Presence features disabled."
110
+ );
111
+ return;
112
+ }
104
113
  const socket = getPresenceSocket();
114
+ let hasJoined = false;
105
115
  const joinRoom = () => {
116
+ if (hasJoined) return;
117
+ hasJoined = true;
106
118
  socket.emit("presence:join", { roomId, user: currentUser });
107
119
  };
108
120
  if (socket.connected) {
109
121
  joinRoom();
122
+ } else {
123
+ socket.once("connect", joinRoom);
110
124
  }
111
- socket.on("connect", joinRoom);
112
125
  const heartbeat = () => {
113
126
  socket.emit("presence:heartbeat", { roomId });
114
127
  };
@@ -134,8 +147,7 @@ function usePresence(options) {
134
147
  };
135
148
  }, [
136
149
  roomId,
137
- currentUser.userId,
138
- currentUser.name,
150
+ currentUserKey,
139
151
  enabled,
140
152
  heartbeatInterval,
141
153
  normalizeUsers2
@@ -157,10 +169,12 @@ function usePresenceWatch(options) {
157
169
  const [roomsUsers, setRoomsUsers] = (0, import_react2.useState)(
158
170
  {}
159
171
  );
172
+ const roomIdsKey = JSON.stringify(roomIds ?? []);
160
173
  const normalizedRoomIds = (0, import_react2.useMemo)(() => {
161
- const uniq = Array.from(new Set((roomIds ?? []).filter(Boolean)));
174
+ const parsed = JSON.parse(roomIdsKey);
175
+ const uniq = Array.from(new Set(parsed.filter(Boolean)));
162
176
  return uniq;
163
- }, [roomIds]);
177
+ }, [roomIdsKey]);
164
178
  const watchedRef = (0, import_react2.useRef)(/* @__PURE__ */ new Set());
165
179
  (0, import_react2.useEffect)(() => {
166
180
  if (!enabled) {
@@ -168,6 +182,12 @@ function usePresenceWatch(options) {
168
182
  watchedRef.current = /* @__PURE__ */ new Set();
169
183
  return;
170
184
  }
185
+ if (!isPresenceSocketInitialized()) {
186
+ console.warn(
187
+ "[presence-watch] Socket not initialized. Watch features disabled."
188
+ );
189
+ return;
190
+ }
171
191
  const socket = getPresenceSocket();
172
192
  const toWatch = normalizedRoomIds.filter((r) => !watchedRef.current.has(r));
173
193
  const toUnwatch = Array.from(watchedRef.current).filter(
@@ -186,10 +206,6 @@ function usePresenceWatch(options) {
186
206
  return next;
187
207
  });
188
208
  }
189
- }, [enabled, normalizedRoomIds]);
190
- (0, import_react2.useEffect)(() => {
191
- if (!enabled) return;
192
- const socket = getPresenceSocket();
193
209
  const handleSnapshot = (payload) => {
194
210
  const rooms = payload?.rooms ?? {};
195
211
  setRoomsUsers((prev) => {
@@ -207,33 +223,38 @@ function usePresenceWatch(options) {
207
223
  if (!watchedRef.current.has(roomId)) return;
208
224
  setRoomsUsers((prev) => ({ ...prev, [roomId]: normalizeUsers(users) }));
209
225
  };
210
- socket.on("presence:snapshot", handleSnapshot);
211
- socket.on("presence:update", handleUpdate);
212
226
  const handleConnect = () => {
213
227
  const ids = Array.from(watchedRef.current);
214
228
  if (ids.length > 0) socket.emit("presence:watch", { roomIds: ids });
215
229
  };
230
+ socket.on("presence:snapshot", handleSnapshot);
231
+ socket.on("presence:update", handleUpdate);
216
232
  socket.on("connect", handleConnect);
217
233
  return () => {
218
234
  socket.off("presence:snapshot", handleSnapshot);
219
235
  socket.off("presence:update", handleUpdate);
220
236
  socket.off("connect", handleConnect);
221
- const ids = Array.from(watchedRef.current);
222
- if (ids.length > 0) socket.emit("presence:unwatch", { roomIds: ids });
237
+ if (normalizedRoomIds.length > 0) {
238
+ socket.emit("presence:unwatch", { roomIds: normalizedRoomIds });
239
+ }
223
240
  watchedRef.current = /* @__PURE__ */ new Set();
224
241
  };
225
- }, [enabled]);
242
+ }, [enabled, normalizedRoomIds]);
226
243
  return roomsUsers;
227
244
  }
228
245
 
229
246
  // src/presence/headless/usePresenceAvatarsState.ts
230
247
  var import_react3 = require("react");
231
- function getInitialFromUser(user) {
248
+
249
+ // src/presence/core/utils.ts
250
+ function getInitial(user) {
232
251
  const base = (user?.name || user?.userId || "").trim();
233
252
  if (!base) return "?";
234
253
  const ch = base.charAt(0);
235
254
  return /[a-z]/i.test(ch) ? ch.toUpperCase() : ch;
236
255
  }
256
+
257
+ // src/presence/headless/usePresenceAvatarsState.ts
237
258
  function usePresenceAvatarsState(options) {
238
259
  const { users, maxVisible = 3 } = options;
239
260
  const safeUsers = Array.isArray(users) ? users : [];
@@ -245,9 +266,6 @@ function usePresenceAvatarsState(options) {
245
266
  const [hoveredIndex, setHoveredIndex] = (0, import_react3.useState)(null);
246
267
  const hoveredUser = hoveredIndex != null ? visibleUsers[hoveredIndex] : null;
247
268
  const baseTopZ = visibleUsers.length + 1;
248
- const getInitial2 = (0, import_react3.useCallback)((user) => {
249
- return getInitialFromUser(user);
250
- }, []);
251
269
  const getZIndex = (0, import_react3.useCallback)(
252
270
  (index, isHovered) => {
253
271
  return isHovered ? 100 : baseTopZ - index;
@@ -260,7 +278,7 @@ function usePresenceAvatarsState(options) {
260
278
  hoveredUser,
261
279
  hoveredIndex,
262
280
  setHoveredIndex,
263
- getInitial: getInitial2,
281
+ getInitial,
264
282
  getZIndex
265
283
  };
266
284
  }
@@ -412,22 +430,19 @@ function usePresenceFloatingState(options) {
412
430
  };
413
431
  }
414
432
  }, [position.top, position.left, hasDragged]);
415
- const onAvatarEnter = (0, import_react4.useCallback)(
416
- (e, user) => {
417
- const container = containerRef.current;
418
- if (!container) {
419
- setHoveredUser(user);
420
- setTooltipTop(0);
421
- return;
422
- }
423
- const containerRect = container.getBoundingClientRect();
424
- const targetRect = e.currentTarget.getBoundingClientRect();
425
- const topWithinContainer = targetRect.top - containerRect.top;
433
+ const onAvatarEnter = (0, import_react4.useCallback)((e, user) => {
434
+ const container = containerRef.current;
435
+ if (!container) {
426
436
  setHoveredUser(user);
427
- setTooltipTop(topWithinContainer);
428
- },
429
- []
430
- );
437
+ setTooltipTop(0);
438
+ return;
439
+ }
440
+ const containerRect = container.getBoundingClientRect();
441
+ const targetRect = e.currentTarget.getBoundingClientRect();
442
+ const topWithinContainer = targetRect.top - containerRect.top;
443
+ setHoveredUser(user);
444
+ setTooltipTop(topWithinContainer);
445
+ }, []);
431
446
  const onAvatarLeave = (0, import_react4.useCallback)(() => {
432
447
  setHoveredUser(null);
433
448
  }, []);
@@ -556,12 +571,6 @@ function PresenceAvatars({
556
571
  // src/presence/components/PresenceFloating.tsx
557
572
  var import_react6 = require("react");
558
573
  var import_jsx_runtime2 = require("react/jsx-runtime");
559
- function getInitial(user) {
560
- const base = (user?.name || user?.userId || "").trim();
561
- if (!base) return "?";
562
- const ch = base.charAt(0);
563
- return /[a-z]/i.test(ch) ? ch.toUpperCase() : ch;
564
- }
565
574
  function PresenceFloating({
566
575
  users,
567
576
  maxVisible = 8,
@@ -671,10 +680,74 @@ function PresenceFloating({
671
680
  }
672
681
  );
673
682
  }
683
+
684
+ // src/presence/components/PresenceProvider.tsx
685
+ var import_react7 = require("react");
686
+ var import_jsx_runtime3 = require("react/jsx-runtime");
687
+ var PresenceContext = (0, import_react7.createContext)(null);
688
+ function PresenceProvider({
689
+ url,
690
+ transports,
691
+ debug = false,
692
+ children
693
+ }) {
694
+ const [status, setStatus] = (0, import_react7.useState)("disconnected");
695
+ const [isInitialized, setIsInitialized] = (0, import_react7.useState)(false);
696
+ (0, import_react7.useEffect)(() => {
697
+ initPresenceSocket({ url, transports });
698
+ setIsInitialized(true);
699
+ if (debug) {
700
+ console.log("[presence] Socket initialized with URL:", url);
701
+ }
702
+ const socket = getPresenceSocket();
703
+ const handleConnect = () => {
704
+ setStatus("connected");
705
+ if (debug) {
706
+ console.log("[presence] Connected");
707
+ }
708
+ };
709
+ const handleDisconnect = () => {
710
+ setStatus("disconnected");
711
+ if (debug) {
712
+ console.log("[presence] Disconnected");
713
+ }
714
+ };
715
+ const handleConnecting = () => {
716
+ setStatus("connecting");
717
+ };
718
+ if (socket.connected) {
719
+ setStatus("connected");
720
+ } else {
721
+ setStatus("connecting");
722
+ }
723
+ socket.on("connect", handleConnect);
724
+ socket.on("disconnect", handleDisconnect);
725
+ socket.io.on("reconnect_attempt", handleConnecting);
726
+ return () => {
727
+ socket.off("connect", handleConnect);
728
+ socket.off("disconnect", handleDisconnect);
729
+ socket.io.off("reconnect_attempt", handleConnecting);
730
+ disconnectPresenceSocket();
731
+ setIsInitialized(false);
732
+ if (debug) {
733
+ console.log("[presence] Socket disconnected and cleaned up");
734
+ }
735
+ };
736
+ }, [url, transports, debug]);
737
+ const contextValue = {
738
+ isInitialized,
739
+ status
740
+ };
741
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PresenceContext.Provider, { value: contextValue, children });
742
+ }
743
+ function usePresenceStatus() {
744
+ return (0, import_react7.useContext)(PresenceContext);
745
+ }
674
746
  // Annotate the CommonJS export names for ESM import in node:
675
747
  0 && (module.exports = {
676
748
  PresenceAvatars,
677
749
  PresenceFloating,
750
+ PresenceProvider,
678
751
  disconnectPresenceSocket,
679
752
  getPresenceSocket,
680
753
  initPresenceSocket,
@@ -682,6 +755,7 @@ function PresenceFloating({
682
755
  usePresence,
683
756
  usePresenceAvatarsState,
684
757
  usePresenceFloatingState,
758
+ usePresenceStatus,
685
759
  usePresenceWatch
686
760
  });
687
761
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/presence/index.ts","../../src/presence/core/socket.ts","../../src/presence/hooks/usePresence.ts","../../src/presence/hooks/usePresenceWatch.ts","../../src/presence/headless/usePresenceAvatarsState.ts","../../src/presence/headless/usePresenceFloatingState.ts","../../src/presence/components/PresenceAvatars.tsx","../../src/presence/components/PresenceFloating.tsx"],"sourcesContent":["// Core exports\nexport {\n initPresenceSocket,\n getPresenceSocket,\n disconnectPresenceSocket,\n isPresenceSocketInitialized,\n} from \"./core/socket\";\n\nexport type {\n PresenceUser,\n PresenceConfig,\n UsePresenceOptions,\n UsePresenceWatchOptions,\n UsePresenceAvatarsStateOptions,\n UsePresenceAvatarsStateReturn,\n UsePresenceFloatingStateOptions,\n UsePresenceFloatingStateReturn,\n // Component props types\n PresenceAvatarsProps,\n PresenceAvatarRenderProps,\n PresenceAvatarsTooltipRenderProps,\n PresenceFloatingProps,\n PresenceFloatingAvatarRenderProps,\n PresenceFloatingTooltipRenderProps,\n} from \"./core/types\";\n\n// Hooks exports\nexport { usePresence } from \"./hooks/usePresence\";\nexport { usePresenceWatch } from \"./hooks/usePresenceWatch\";\n\n// Headless state exports\nexport { usePresenceAvatarsState } from \"./headless/usePresenceAvatarsState\";\nexport { usePresenceFloatingState } from \"./headless/usePresenceFloatingState\";\n\n// Component exports\nexport { PresenceAvatars } from \"./components/PresenceAvatars\";\nexport { PresenceFloating } from \"./components/PresenceFloating\";\n","import { io, Socket } from 'socket.io-client';\nimport type { PresenceConfig } from './types';\n\nlet config: PresenceConfig | null = null;\nlet sharedSocket: Socket | null = null;\n\n/**\n * Initialize the presence socket with configuration\n * Must be called before using any presence hooks\n */\nexport function initPresenceSocket(presenceConfig: PresenceConfig): void {\n config = presenceConfig;\n\n // If socket already exists, disconnect and recreate\n if (sharedSocket) {\n sharedSocket.disconnect();\n sharedSocket = null;\n }\n}\n\n/**\n * Get the shared socket instance\n * @throws Error if socket is not initialized\n */\nexport function getPresenceSocket(): Socket {\n if (!config) {\n throw new Error(\n '[presence-module] Socket not initialized. Call initPresenceSocket() first.',\n );\n }\n\n if (sharedSocket && sharedSocket.connected) {\n return sharedSocket;\n }\n\n if (!sharedSocket) {\n sharedSocket = io(config.url, {\n transports: config.transports ?? ['websocket'],\n });\n } else if (!sharedSocket.connected) {\n sharedSocket.connect();\n }\n\n return sharedSocket;\n}\n\n/**\n * Disconnect and cleanup the socket connection\n */\nexport function disconnectPresenceSocket(): void {\n if (sharedSocket) {\n sharedSocket.disconnect();\n sharedSocket = null;\n }\n}\n\n/**\n * Check if socket is initialized\n */\nexport function isPresenceSocketInitialized(): boolean {\n return config !== null;\n}\n","import { useEffect, useState, useCallback } from 'react';\nimport { getPresenceSocket } from '../core/socket';\nimport type { PresenceUser, UsePresenceOptions } from '../core/types';\n\nconst DEFAULT_HEARTBEAT_INTERVAL = 10 * 60 * 1000; // 10 minutes\n\n/**\n * Hook to join a presence room and track users in that room\n *\n * @param options - Configuration options\n * @returns Array of users currently in the room\n *\n * @example\n * ```tsx\n * const users = usePresence({\n * roomId: 'my-app:page-123',\n * currentUser: { userId: '1', name: 'John' },\n * });\n * ```\n */\nexport function usePresence(options: UsePresenceOptions): PresenceUser[] {\n const {\n roomId,\n currentUser,\n enabled = true,\n heartbeatInterval = DEFAULT_HEARTBEAT_INTERVAL,\n } = options;\n\n const [users, setUsers] = useState<PresenceUser[]>([]);\n\n // Normalize users from server response\n const normalizeUsers = useCallback((incoming: unknown): PresenceUser[] => {\n if (Array.isArray(incoming)) {\n return incoming as PresenceUser[];\n }\n if (\n incoming &&\n typeof incoming === 'object' &&\n Array.isArray((incoming as { users?: unknown }).users)\n ) {\n return (incoming as { users: unknown[] }).users as PresenceUser[];\n }\n return [];\n }, []);\n\n // Clear users when disabled\n useEffect(() => {\n if (!enabled) {\n setUsers([]);\n }\n }, [enabled]);\n\n useEffect(() => {\n if (!enabled) {\n return;\n }\n\n const socket = getPresenceSocket();\n\n const joinRoom = () => {\n socket.emit('presence:join', { roomId, user: currentUser });\n };\n\n // Join immediately if connected, otherwise wait for connect event\n if (socket.connected) {\n joinRoom();\n }\n\n socket.on('connect', joinRoom);\n\n // Heartbeat to extend TTL\n const heartbeat = () => {\n socket.emit('presence:heartbeat', { roomId });\n };\n heartbeat();\n const heartbeatIntervalId = window.setInterval(\n heartbeat,\n heartbeatInterval,\n );\n\n // Handle presence updates from server\n const handlePresenceUpdate = (incoming: unknown) => {\n setUsers(normalizeUsers(incoming));\n };\n socket.on('presence:update', handlePresenceUpdate);\n\n // Notify server when tab/window closes\n const handleBeforeUnload = () => {\n socket.emit('presence:leave', { roomId, userId: currentUser.userId });\n };\n window.addEventListener('beforeunload', handleBeforeUnload);\n\n return () => {\n socket.off('connect', joinRoom);\n socket.off('presence:update', handlePresenceUpdate);\n window.removeEventListener('beforeunload', handleBeforeUnload);\n window.clearInterval(heartbeatIntervalId);\n socket.emit('presence:leave', { roomId, userId: currentUser.userId });\n };\n }, [\n roomId,\n currentUser.userId,\n currentUser.name,\n enabled,\n heartbeatInterval,\n normalizeUsers,\n ]);\n\n return users;\n}\n","import { useEffect, useMemo, useRef, useState, useCallback } from 'react';\nimport { getPresenceSocket } from '../core/socket';\nimport type { PresenceUser, UsePresenceWatchOptions } from '../core/types';\n\ntype SnapshotPayload = {\n rooms?: Record<string, unknown>;\n};\n\ntype UpdatePayload = {\n roomId?: string;\n users?: unknown;\n};\n\nfunction normalizeUsers(value: unknown): PresenceUser[] {\n if (Array.isArray(value)) return value as PresenceUser[];\n if (\n value &&\n typeof value === 'object' &&\n Array.isArray((value as { users?: unknown }).users)\n ) {\n return (value as { users: unknown[] }).users as PresenceUser[];\n }\n return [];\n}\n\n/**\n * Hook to watch multiple rooms without joining them\n * (read-only observation, current user is not added to the rooms)\n *\n * @param options - Configuration options\n * @returns Map of roomId to array of users in that room\n *\n * @example\n * ```tsx\n * const roomsUsers = usePresenceWatch({\n * roomIds: ['room-1', 'room-2', 'room-3'],\n * });\n * // roomsUsers = { 'room-1': [...], 'room-2': [...], ... }\n * ```\n */\nexport function usePresenceWatch(\n options: UsePresenceWatchOptions,\n): Record<string, PresenceUser[]> {\n const { roomIds, enabled = true } = options;\n\n const [roomsUsers, setRoomsUsers] = useState<Record<string, PresenceUser[]>>(\n {},\n );\n\n const normalizedRoomIds = useMemo(() => {\n const uniq = Array.from(new Set((roomIds ?? []).filter(Boolean)));\n return uniq;\n }, [roomIds]);\n\n const watchedRef = useRef<Set<string>>(new Set());\n\n // Handle watch/unwatch based on roomIds changes\n useEffect(() => {\n if (!enabled) {\n setRoomsUsers({});\n watchedRef.current = new Set();\n return;\n }\n\n const socket = getPresenceSocket();\n\n const toWatch = normalizedRoomIds.filter((r) => !watchedRef.current.has(r));\n const toUnwatch = Array.from(watchedRef.current).filter(\n (r) => !normalizedRoomIds.includes(r),\n );\n\n if (toWatch.length > 0) {\n socket.emit('presence:watch', { roomIds: toWatch });\n for (const r of toWatch) watchedRef.current.add(r);\n }\n\n if (toUnwatch.length > 0) {\n socket.emit('presence:unwatch', { roomIds: toUnwatch });\n for (const r of toUnwatch) watchedRef.current.delete(r);\n setRoomsUsers((prev) => {\n const next = { ...prev };\n for (const r of toUnwatch) delete next[r];\n return next;\n });\n }\n }, [enabled, normalizedRoomIds]);\n\n // Handle socket events\n useEffect(() => {\n if (!enabled) return;\n\n const socket = getPresenceSocket();\n\n const handleSnapshot = (payload: SnapshotPayload) => {\n const rooms = payload?.rooms ?? {};\n setRoomsUsers((prev) => {\n const next = { ...prev };\n for (const [roomId, users] of Object.entries(rooms)) {\n next[roomId] = normalizeUsers(users);\n }\n return next;\n });\n };\n\n const handleUpdate = (payload: unknown) => {\n if (!payload || typeof payload !== 'object') return;\n const { roomId, users } = payload as UpdatePayload;\n if (!roomId) return;\n if (!watchedRef.current.has(roomId)) return;\n setRoomsUsers((prev) => ({ ...prev, [roomId]: normalizeUsers(users) }));\n };\n\n socket.on('presence:snapshot', handleSnapshot);\n socket.on('presence:update', handleUpdate);\n\n // Re-watch on reconnect\n const handleConnect = () => {\n const ids = Array.from(watchedRef.current);\n if (ids.length > 0) socket.emit('presence:watch', { roomIds: ids });\n };\n socket.on('connect', handleConnect);\n\n return () => {\n socket.off('presence:snapshot', handleSnapshot);\n socket.off('presence:update', handleUpdate);\n socket.off('connect', handleConnect);\n\n const ids = Array.from(watchedRef.current);\n if (ids.length > 0) socket.emit('presence:unwatch', { roomIds: ids });\n watchedRef.current = new Set();\n };\n }, [enabled]);\n\n return roomsUsers;\n}\n","import { useMemo, useState, useCallback } from 'react';\nimport type {\n PresenceUser,\n UsePresenceAvatarsStateOptions,\n UsePresenceAvatarsStateReturn,\n} from '../core/types';\n\n/**\n * Extract initial character from user name/id\n */\nfunction getInitialFromUser(user: PresenceUser): string {\n const base = (user?.name || user?.userId || '').trim();\n if (!base) return '?';\n const ch = base.charAt(0);\n return /[a-z]/i.test(ch) ? ch.toUpperCase() : ch;\n}\n\n/**\n * Headless hook for presence avatars UI state management\n *\n * This hook provides all the state and logic needed to build\n * a presence avatars component without any styling.\n *\n * @param options - Configuration options\n * @returns State and handlers for building presence avatars UI\n *\n * @example\n * ```tsx\n * const {\n * visibleUsers,\n * moreCount,\n * hoveredUser,\n * setHoveredIndex,\n * getInitial,\n * getZIndex,\n * } = usePresenceAvatarsState({ users, maxVisible: 3 });\n *\n * return (\n * <div>\n * {visibleUsers.map((user, idx) => (\n * <span\n * key={user.userId}\n * style={{ zIndex: getZIndex(idx, hoveredUser?.userId === user.userId) }}\n * onMouseEnter={() => setHoveredIndex(idx)}\n * onMouseLeave={() => setHoveredIndex(null)}\n * >\n * {getInitial(user)}\n * </span>\n * ))}\n * {moreCount > 0 && <span>+{moreCount}</span>}\n * </div>\n * );\n * ```\n */\nexport function usePresenceAvatarsState(\n options: UsePresenceAvatarsStateOptions,\n): UsePresenceAvatarsStateReturn {\n const { users, maxVisible = 3 } = options;\n\n const safeUsers = Array.isArray(users) ? users : [];\n\n const visibleUsers = useMemo(\n () => safeUsers.slice(0, maxVisible),\n [safeUsers, maxVisible],\n );\n\n const moreCount = Math.max(0, safeUsers.length - visibleUsers.length);\n\n const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);\n\n const hoveredUser = hoveredIndex != null ? visibleUsers[hoveredIndex] : null;\n\n const baseTopZ = visibleUsers.length + 1;\n\n const getInitial = useCallback((user: PresenceUser): string => {\n return getInitialFromUser(user);\n }, []);\n\n const getZIndex = useCallback(\n (index: number, isHovered: boolean): number => {\n return isHovered ? 100 : baseTopZ - index;\n },\n [baseTopZ],\n );\n\n return {\n visibleUsers,\n moreCount,\n hoveredUser,\n hoveredIndex,\n setHoveredIndex,\n getInitial,\n getZIndex,\n };\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type {\n PresenceUser,\n UsePresenceFloatingStateOptions,\n UsePresenceFloatingStateReturn,\n} from \"../core/types\";\n\n/**\n * Headless hook for presence floating UI state management\n *\n * This hook provides all the state and logic needed to build\n * a draggable floating presence panel without any styling.\n *\n * @param options - Configuration options\n * @returns State and handlers for building floating presence UI\n *\n * @example\n * ```tsx\n * const {\n * containerRef,\n * inlineStyle,\n * isDragging,\n * visibleUsers,\n * moreCount,\n * hoveredUser,\n * tooltipTop,\n * onMouseDownHeader,\n * onAvatarEnter,\n * onAvatarLeave,\n * } = usePresenceFloatingState({ users, maxVisible: 8 });\n *\n * return (\n * <div ref={containerRef} style={inlineStyle} onMouseDown={onMouseDownHeader}>\n * <div className=\"header\">열람중 {users.length}</div>\n * <div className=\"body\">\n * {visibleUsers.map((user) => (\n * <span\n * key={user.userId}\n * onMouseEnter={(e) => onAvatarEnter(e, user)}\n * onMouseLeave={onAvatarLeave}\n * >\n * {user.name}\n * </span>\n * ))}\n * </div>\n * </div>\n * );\n * ```\n */\nexport function usePresenceFloatingState(\n options: UsePresenceFloatingStateOptions\n): UsePresenceFloatingStateReturn {\n const { users, maxVisible = 8, initialPosition = { top: 166 } } = options;\n\n const containerRef = useRef<HTMLDivElement>(null);\n const [dragging, setDragging] = useState(false);\n const [hasDragged, setHasDragged] = useState(false);\n const [dragOffset, setDragOffset] = useState<{ x: number; y: number }>({\n x: 0,\n y: 0,\n });\n const [position, setPosition] = useState<{ top: number; left: number }>({\n top: initialPosition.top,\n left: 0,\n });\n const [hoveredUser, setHoveredUser] = useState<PresenceUser | null>(null);\n const [tooltipTop, setTooltipTop] = useState<number>(0);\n\n const safeUsers = Array.isArray(users) ? users : [];\n const visibleUsers = safeUsers.slice(0, maxVisible);\n const moreCount = Math.max(0, safeUsers.length - visibleUsers.length);\n\n // Position adjustment after drag (resize handling)\n useEffect(() => {\n if (!hasDragged) return;\n\n const el = containerRef.current;\n if (!el) return;\n\n const adjustPosition = () => {\n const rect = el.getBoundingClientRect();\n const maxLeft = window.innerWidth - rect.width;\n const maxTop = window.innerHeight - rect.height - 8;\n setPosition((prev) => ({\n left: Math.min(Math.max(0, prev.left), Math.max(0, maxLeft)),\n top: Math.min(Math.max(8, prev.top), Math.max(8, maxTop)),\n }));\n };\n\n const resizeObserver = new ResizeObserver(() => {\n adjustPosition();\n });\n resizeObserver.observe(el);\n\n window.addEventListener(\"resize\", adjustPosition);\n\n return () => {\n resizeObserver.disconnect();\n window.removeEventListener(\"resize\", adjustPosition);\n };\n }, [hasDragged]);\n\n // Helper to get coordinates from mouse or touch event\n const getEventCoordinates = useCallback(\n (e: MouseEvent | TouchEvent | React.MouseEvent | React.TouchEvent) => {\n if (\"touches\" in e && e.touches.length > 0) {\n return { clientX: e.touches[0].clientX, clientY: e.touches[0].clientY };\n }\n if (\"changedTouches\" in e && e.changedTouches.length > 0) {\n return {\n clientX: e.changedTouches[0].clientX,\n clientY: e.changedTouches[0].clientY,\n };\n }\n if (\"clientX\" in e) {\n return { clientX: e.clientX, clientY: e.clientY };\n }\n return { clientX: 0, clientY: 0 };\n },\n []\n );\n\n const onMouseDownHeader = useCallback(\n (e: React.MouseEvent) => {\n const el = containerRef.current;\n if (!el) return;\n const rect = el.getBoundingClientRect();\n\n // On first drag, switch to left-based positioning\n if (!hasDragged) {\n setHasDragged(true);\n setPosition({\n top: rect.top,\n left: rect.left,\n });\n }\n\n setDragOffset({ x: e.clientX - rect.left, y: e.clientY - rect.top });\n setDragging(true);\n e.preventDefault();\n },\n [hasDragged]\n );\n\n const onTouchStartHeader = useCallback(\n (e: React.TouchEvent) => {\n const el = containerRef.current;\n if (!el) return;\n const rect = el.getBoundingClientRect();\n const { clientX, clientY } = getEventCoordinates(e);\n\n // On first drag, switch to left-based positioning\n if (!hasDragged) {\n setHasDragged(true);\n setPosition({\n top: rect.top,\n left: rect.left,\n });\n }\n\n setDragOffset({ x: clientX - rect.left, y: clientY - rect.top });\n setDragging(true);\n // Note: Don't prevent default to allow scrolling detection\n },\n [hasDragged, getEventCoordinates]\n );\n\n // Handle drag movement (mouse and touch)\n useEffect(() => {\n if (!dragging) return;\n\n const el = containerRef.current;\n\n const onMove = (e: MouseEvent | TouchEvent) => {\n if (!el) return;\n const rect = el.getBoundingClientRect();\n const { clientX, clientY } = getEventCoordinates(e);\n const newLeft = clientX - dragOffset.x;\n const newTop = clientY - dragOffset.y;\n const maxLeft = window.innerWidth - rect.width;\n const maxTop = window.innerHeight - rect.height - 8;\n setPosition({\n left: Math.min(Math.max(0, newLeft), Math.max(0, maxLeft)),\n top: Math.min(Math.max(8, newTop), Math.max(8, maxTop)),\n });\n\n // Prevent scrolling while dragging on touch\n if (\"touches\" in e) {\n e.preventDefault();\n }\n };\n\n const onUp = () => setDragging(false);\n\n // Mouse events\n window.addEventListener(\"mousemove\", onMove);\n window.addEventListener(\"mouseup\", onUp, { once: true });\n\n // Touch events\n window.addEventListener(\"touchmove\", onMove, { passive: false });\n window.addEventListener(\"touchend\", onUp, { once: true });\n window.addEventListener(\"touchcancel\", onUp, { once: true });\n\n return () => {\n window.removeEventListener(\"mousemove\", onMove);\n window.removeEventListener(\"mouseup\", onUp);\n window.removeEventListener(\"touchmove\", onMove);\n window.removeEventListener(\"touchend\", onUp);\n window.removeEventListener(\"touchcancel\", onUp);\n };\n }, [dragging, dragOffset.x, dragOffset.y, getEventCoordinates]);\n\n const inlineStyle = useMemo<React.CSSProperties>(() => {\n if (hasDragged) {\n return {\n position: \"fixed\",\n top: position.top,\n left: position.left,\n right: \"auto\",\n };\n } else {\n return {\n position: \"fixed\",\n top: position.top,\n right: 0,\n left: \"auto\",\n };\n }\n }, [position.top, position.left, hasDragged]);\n\n const onAvatarEnter = useCallback(\n (e: React.MouseEvent, user: PresenceUser) => {\n const container = containerRef.current;\n if (!container) {\n setHoveredUser(user);\n setTooltipTop(0);\n return;\n }\n const containerRect = container.getBoundingClientRect();\n const targetRect = (\n e.currentTarget as HTMLElement\n ).getBoundingClientRect();\n const topWithinContainer = targetRect.top - containerRect.top;\n setHoveredUser(user);\n setTooltipTop(topWithinContainer);\n },\n []\n );\n\n const onAvatarLeave = useCallback(() => {\n setHoveredUser(null);\n }, []);\n\n return {\n containerRef,\n position,\n isDragging: dragging,\n hasDragged,\n inlineStyle,\n visibleUsers,\n moreCount,\n hoveredUser,\n tooltipTop,\n onMouseDownHeader,\n onTouchStartHeader,\n onAvatarEnter,\n onAvatarLeave,\n };\n}\n","import React, { useState, useCallback } from \"react\";\nimport { usePresenceAvatarsState } from \"../headless/usePresenceAvatarsState\";\nimport type {\n PresenceUser,\n PresenceAvatarsProps,\n PresenceAvatarRenderProps,\n PresenceAvatarsTooltipRenderProps,\n} from \"../core/types\";\n\n/**\n * PresenceAvatars - Displays a stack of user avatars with hover tooltips\n *\n * @example\n * ```tsx\n * // Basic usage\n * <PresenceAvatars users={users} />\n *\n * // Custom avatar\n * <PresenceAvatars\n * users={users}\n * renderAvatar={({ user, initial }) => (\n * <img src={user.avatarUrl} alt={user.name} />\n * )}\n * />\n *\n * // Custom tooltip\n * <PresenceAvatars\n * users={users}\n * renderTooltip={({ user, position }) => (\n * <div style={{ top: position.top, left: position.left }}>\n * {user.name}\n * </div>\n * )}\n * />\n * ```\n */\nexport function PresenceAvatars({\n users,\n maxVisible = 3,\n className,\n renderAvatar,\n renderTooltip,\n renderMore,\n}: PresenceAvatarsProps) {\n const {\n visibleUsers,\n moreCount,\n hoveredUser,\n hoveredIndex,\n setHoveredIndex,\n getInitial,\n getZIndex,\n } = usePresenceAvatarsState({ users, maxVisible });\n\n const [tooltipPos, setTooltipPos] = useState<{\n top: number;\n left: number;\n } | null>(null);\n\n const updateTooltipPos = useCallback((el: HTMLElement) => {\n const rect = el.getBoundingClientRect();\n const tooltipWidth = 200;\n const half = tooltipWidth / 2;\n const centerX = rect.left + rect.width / 2;\n const left = Math.min(\n Math.max(centerX, half + 8),\n window.innerWidth - half - 8\n );\n const top = Math.min(rect.bottom + 8, window.innerHeight - 8);\n setTooltipPos({ top, left });\n }, []);\n\n const handleMouseEnter = useCallback(\n (e: React.MouseEvent, idx: number) => {\n setHoveredIndex(idx);\n updateTooltipPos(e.currentTarget as HTMLElement);\n },\n [setHoveredIndex, updateTooltipPos]\n );\n\n const handleMouseLeave = useCallback(() => {\n setHoveredIndex(null);\n }, [setHoveredIndex]);\n\n // Default tooltip renderer\n const defaultTooltipRenderer = useCallback(\n ({ user, position }: PresenceAvatarsTooltipRenderProps) => (\n <div\n className=\"presence-avatars__tooltip\"\n style={{ top: position.top, left: position.left }}\n >\n <div className=\"presence-avatars__tooltip-row\">\n <span className=\"presence-avatars__tooltip-key\">Name</span>\n <span className=\"presence-avatars__tooltip-val\">\n {user.name || \"-\"}\n </span>\n </div>\n <div className=\"presence-avatars__tooltip-row\">\n <span className=\"presence-avatars__tooltip-key\">ID</span>\n <span className=\"presence-avatars__tooltip-val\">\n {user.userId || \"-\"}\n </span>\n </div>\n </div>\n ),\n []\n );\n\n // Default avatar renderer\n const defaultAvatarRenderer = useCallback(\n ({ initial, isHovered }: PresenceAvatarRenderProps) => <>{initial}</>,\n []\n );\n\n return (\n <div className={`presence-avatars ${className ?? \"\"}`}>\n <div className=\"presence-avatars__stack\">\n {visibleUsers.map((user, idx) => {\n const isHovered = hoveredIndex === idx;\n const initial = getInitial(user);\n const zIndex = getZIndex(idx, isHovered);\n\n const renderProps: PresenceAvatarRenderProps = {\n user,\n index: idx,\n initial,\n isHovered,\n zIndex,\n };\n\n return (\n <span\n key={`${user.userId}-${idx}`}\n className={`presence-avatars__avatar ${\n isHovered ? \"is-hovered\" : \"\"\n }`}\n style={{ zIndex }}\n onMouseEnter={(e) => handleMouseEnter(e, idx)}\n onMouseLeave={handleMouseLeave}\n aria-label={user.name || user.userId || \"\"}\n >\n {renderAvatar\n ? renderAvatar(renderProps)\n : defaultAvatarRenderer(renderProps)}\n </span>\n );\n })}\n {moreCount > 0 &&\n (renderMore ? (\n renderMore(moreCount)\n ) : (\n <span className=\"presence-avatars__more\">+{moreCount}</span>\n ))}\n </div>\n\n {hoveredUser &&\n tooltipPos &&\n (renderTooltip\n ? renderTooltip({ user: hoveredUser, position: tooltipPos })\n : defaultTooltipRenderer({\n user: hoveredUser,\n position: tooltipPos,\n }))}\n </div>\n );\n}\n\nexport default PresenceAvatars;\n","import React, { useCallback } from \"react\";\nimport { usePresenceFloatingState } from \"../headless/usePresenceFloatingState\";\nimport type {\n PresenceUser,\n PresenceFloatingProps,\n PresenceFloatingAvatarRenderProps,\n PresenceFloatingTooltipRenderProps,\n} from \"../core/types\";\n\n/**\n * Extract initial character from user name/id\n */\nfunction getInitial(user: PresenceUser): string {\n const base = (user?.name || user?.userId || \"\").trim();\n if (!base) return \"?\";\n const ch = base.charAt(0);\n return /[a-z]/i.test(ch) ? ch.toUpperCase() : ch;\n}\n\n/**\n * PresenceFloating - Draggable floating panel showing presence users\n *\n * @example\n * ```tsx\n * // Basic usage\n * <PresenceFloating users={users} />\n *\n * // Custom text\n * <PresenceFloating\n * users={users}\n * title=\"Viewing\"\n * emptyText=\"No one\"\n * moreText={(n) => `+${n} more`}\n * />\n *\n * // Custom avatar\n * <PresenceFloating\n * users={users}\n * renderAvatar={({ user, initial }) => (\n * <img src={user.avatarUrl} alt={user.name} />\n * )}\n * />\n * ```\n */\nexport function PresenceFloating({\n users,\n maxVisible = 8,\n className,\n style,\n initialPosition,\n title = \"열람중\",\n emptyText = \"없음\",\n moreText = (n) => `+${n}명`,\n renderAvatar,\n renderTooltip,\n}: PresenceFloatingProps) {\n const {\n containerRef,\n inlineStyle,\n isDragging,\n visibleUsers,\n moreCount,\n hoveredUser,\n tooltipTop,\n onMouseDownHeader,\n onTouchStartHeader,\n onAvatarEnter,\n onAvatarLeave,\n } = usePresenceFloatingState({ users, maxVisible, initialPosition });\n\n // Default tooltip renderer\n const defaultTooltipRenderer = useCallback(\n ({ user, top }: PresenceFloatingTooltipRenderProps) => (\n <div\n className=\"presence-floating__tooltip\"\n style={{ top: Math.max(0, top - 6) }}\n onMouseLeave={onAvatarLeave}\n >\n <div className=\"presence-floating__tooltip-row\">\n <span className=\"presence-floating__tooltip-key\">Name</span>\n <span className=\"presence-floating__tooltip-val\">\n {user.name || \"-\"}\n </span>\n </div>\n <div className=\"presence-floating__tooltip-row\">\n <span className=\"presence-floating__tooltip-key\">ID</span>\n <span className=\"presence-floating__tooltip-val\">\n {user.userId || \"-\"}\n </span>\n </div>\n </div>\n ),\n [onAvatarLeave]\n );\n\n // Default avatar renderer\n const defaultAvatarRenderer = useCallback(\n ({ initial }: PresenceFloatingAvatarRenderProps) => <>{initial}</>,\n []\n );\n\n const combinedStyle: React.CSSProperties = {\n ...inlineStyle,\n ...style,\n };\n\n return (\n <div\n ref={containerRef}\n className={`presence-floating ${isDragging ? \"is-dragging\" : \"\"} ${\n className ?? \"\"\n }`}\n style={combinedStyle}\n >\n <div\n className=\"presence-floating__header\"\n onMouseDown={onMouseDownHeader}\n onTouchStart={onTouchStartHeader}\n >\n <span\n className=\"presence-floating__title\"\n title={title}\n aria-label={title}\n >\n {title}\n </span>\n <span className=\"presence-floating__count\">{users.length}</span>\n </div>\n\n {hoveredUser &&\n (renderTooltip\n ? renderTooltip({ user: hoveredUser, top: tooltipTop })\n : defaultTooltipRenderer({ user: hoveredUser, top: tooltipTop }))}\n\n <div className=\"presence-floating__body\">\n {visibleUsers.length === 0 ? (\n <span className=\"presence-floating__empty\">{emptyText}</span>\n ) : (\n visibleUsers.map((user, idx) => {\n const initial = getInitial(user);\n const label = user.name || user.userId || \"\";\n\n const renderProps: PresenceFloatingAvatarRenderProps = {\n user,\n initial,\n };\n\n return (\n <span\n key={`${user.userId}-${idx}`}\n className=\"presence-floating__avatar\"\n title={label}\n aria-label={label}\n onMouseEnter={(e) => onAvatarEnter(e, user)}\n onMouseLeave={onAvatarLeave}\n >\n {renderAvatar\n ? renderAvatar(renderProps)\n : defaultAvatarRenderer(renderProps)}\n </span>\n );\n })\n )}\n {moreCount > 0 && (\n <span className=\"presence-floating__more\">{moreText(moreCount)}</span>\n )}\n </div>\n </div>\n );\n}\n\nexport default PresenceFloating;\n"],"mappings":"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","names":["normalizeUsers","import_react","import_react","getInitial","import_react","import_react","getInitial","import_react","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../../src/presence/index.ts","../../src/presence/core/socket.ts","../../src/presence/hooks/usePresence.ts","../../src/presence/hooks/usePresenceWatch.ts","../../src/presence/headless/usePresenceAvatarsState.ts","../../src/presence/core/utils.ts","../../src/presence/headless/usePresenceFloatingState.ts","../../src/presence/components/PresenceAvatars.tsx","../../src/presence/components/PresenceFloating.tsx","../../src/presence/components/PresenceProvider.tsx"],"sourcesContent":["// Core exports\nexport {\n initPresenceSocket,\n getPresenceSocket,\n disconnectPresenceSocket,\n isPresenceSocketInitialized,\n} from \"./core/socket\";\n\nexport type {\n PresenceUser,\n PresenceConfig,\n PresenceProviderProps,\n UsePresenceOptions,\n UsePresenceWatchOptions,\n UsePresenceAvatarsStateOptions,\n UsePresenceAvatarsStateReturn,\n UsePresenceFloatingStateOptions,\n UsePresenceFloatingStateReturn,\n // Component props types\n PresenceAvatarsProps,\n PresenceAvatarRenderProps,\n PresenceAvatarsTooltipRenderProps,\n PresenceFloatingProps,\n PresenceFloatingAvatarRenderProps,\n PresenceFloatingTooltipRenderProps,\n} from \"./core/types\";\n\n// Hooks exports\nexport { usePresence } from \"./hooks/usePresence\";\nexport { usePresenceWatch } from \"./hooks/usePresenceWatch\";\n\n// Headless state exports\nexport { usePresenceAvatarsState } from \"./headless/usePresenceAvatarsState\";\nexport { usePresenceFloatingState } from \"./headless/usePresenceFloatingState\";\n\n// Component exports\nexport { PresenceAvatars } from \"./components/PresenceAvatars\";\nexport { PresenceFloating } from \"./components/PresenceFloating\";\nexport {\n PresenceProvider,\n usePresenceStatus,\n} from \"./components/PresenceProvider\";\n","import { io, Socket } from 'socket.io-client';\nimport type { PresenceConfig } from './types';\n\nlet config: PresenceConfig | null = null;\nlet sharedSocket: Socket | null = null;\n\n/**\n * Initialize the presence socket with configuration\n * Must be called before using any presence hooks\n */\nexport function initPresenceSocket(presenceConfig: PresenceConfig): void {\n config = presenceConfig;\n\n // If socket already exists, disconnect and recreate\n if (sharedSocket) {\n sharedSocket.disconnect();\n sharedSocket = null;\n }\n}\n\n/**\n * Get the shared socket instance\n * @throws Error if socket is not initialized\n */\nexport function getPresenceSocket(): Socket {\n if (!config) {\n throw new Error(\n '[presence-module] Socket not initialized. Call initPresenceSocket() first.',\n );\n }\n\n if (sharedSocket && sharedSocket.connected) {\n return sharedSocket;\n }\n\n if (!sharedSocket) {\n sharedSocket = io(config.url, {\n transports: config.transports ?? ['websocket'],\n });\n } else if (!sharedSocket.connected) {\n sharedSocket.connect();\n }\n\n return sharedSocket;\n}\n\n/**\n * Disconnect and cleanup the socket connection\n */\nexport function disconnectPresenceSocket(): void {\n if (sharedSocket) {\n sharedSocket.disconnect();\n sharedSocket = null;\n }\n}\n\n/**\n * Check if socket is initialized\n */\nexport function isPresenceSocketInitialized(): boolean {\n return config !== null;\n}\n","import { useEffect, useState, useCallback, useRef } from \"react\";\nimport { getPresenceSocket, isPresenceSocketInitialized } from \"../core/socket\";\nimport type { PresenceUser, UsePresenceOptions } from \"../core/types\";\n\nconst DEFAULT_HEARTBEAT_INTERVAL = 10 * 60 * 1000; // 10 minutes\n\n/**\n * Hook to join a presence room and track users in that room\n *\n * @param options - Configuration options\n * @returns Array of users currently in the room\n *\n * @example\n * ```tsx\n * const users = usePresence({\n * roomId: 'my-app:page-123',\n * currentUser: { userId: '1', name: 'John' },\n * });\n * ```\n */\nexport function usePresence(options: UsePresenceOptions): PresenceUser[] {\n const {\n roomId,\n currentUser,\n enabled = true,\n heartbeatInterval = DEFAULT_HEARTBEAT_INTERVAL,\n } = options;\n\n const [users, setUsers] = useState<PresenceUser[]>([]);\n\n // Normalize users from server response\n const normalizeUsers = useCallback((incoming: unknown): PresenceUser[] => {\n if (Array.isArray(incoming)) {\n return incoming as PresenceUser[];\n }\n if (\n incoming &&\n typeof incoming === \"object\" &&\n Array.isArray((incoming as { users?: unknown }).users)\n ) {\n return (incoming as { users: unknown[] }).users as PresenceUser[];\n }\n return [];\n }, []);\n\n // Clear users when disabled\n useEffect(() => {\n if (!enabled) {\n setUsers([]);\n }\n }, [enabled]);\n\n // Serialize currentUser to detect deep changes without infinite loops\n const currentUserKey = JSON.stringify(currentUser);\n\n useEffect(() => {\n if (!enabled) {\n return;\n }\n\n if (!isPresenceSocketInitialized()) {\n console.warn(\n \"[presence] Socket not initialized. Presence features disabled.\",\n );\n return;\n }\n\n const socket = getPresenceSocket();\n let hasJoined = false;\n\n const joinRoom = () => {\n if (hasJoined) return; // 중복 join 방지\n hasJoined = true;\n socket.emit(\"presence:join\", { roomId, user: currentUser });\n };\n\n // Join immediately if connected, otherwise wait for connect event (once)\n if (socket.connected) {\n joinRoom();\n } else {\n socket.once(\"connect\", joinRoom); // on → once로 변경하여 중복 방지\n }\n\n // Heartbeat to extend TTL\n const heartbeat = () => {\n socket.emit(\"presence:heartbeat\", { roomId });\n };\n heartbeat();\n const heartbeatIntervalId = window.setInterval(\n heartbeat,\n heartbeatInterval,\n );\n\n // Handle presence updates from server\n const handlePresenceUpdate = (incoming: unknown) => {\n setUsers(normalizeUsers(incoming));\n };\n socket.on(\"presence:update\", handlePresenceUpdate);\n\n // Notify server when tab/window closes\n const handleBeforeUnload = () => {\n socket.emit(\"presence:leave\", { roomId, userId: currentUser.userId });\n };\n window.addEventListener(\"beforeunload\", handleBeforeUnload);\n\n return () => {\n socket.off(\"connect\", joinRoom);\n socket.off(\"presence:update\", handlePresenceUpdate);\n window.removeEventListener(\"beforeunload\", handleBeforeUnload);\n window.clearInterval(heartbeatIntervalId);\n socket.emit(\"presence:leave\", { roomId, userId: currentUser.userId });\n };\n }, [\n roomId,\n currentUserKey,\n enabled,\n heartbeatInterval,\n normalizeUsers,\n ]);\n\n return users;\n}\n","import { useEffect, useMemo, useRef, useState, useCallback } from \"react\";\nimport { getPresenceSocket, isPresenceSocketInitialized } from \"../core/socket\";\nimport type { PresenceUser, UsePresenceWatchOptions } from \"../core/types\";\n\ntype SnapshotPayload = {\n rooms?: Record<string, unknown>;\n};\n\ntype UpdatePayload = {\n roomId?: string;\n users?: unknown;\n};\n\nfunction normalizeUsers(value: unknown): PresenceUser[] {\n if (Array.isArray(value)) return value as PresenceUser[];\n if (\n value &&\n typeof value === \"object\" &&\n Array.isArray((value as { users?: unknown }).users)\n ) {\n return (value as { users: unknown[] }).users as PresenceUser[];\n }\n return [];\n}\n\n/**\n * Hook to watch multiple rooms without joining them\n * (read-only observation, current user is not added to the rooms)\n *\n * @param options - Configuration options\n * @returns Map of roomId to array of users in that room\n *\n * @example\n * ```tsx\n * const roomsUsers = usePresenceWatch({\n * roomIds: ['room-1', 'room-2', 'room-3'],\n * });\n * // roomsUsers = { 'room-1': [...], 'room-2': [...], ... }\n * ```\n */\nexport function usePresenceWatch(\n options: UsePresenceWatchOptions,\n): Record<string, PresenceUser[]> {\n const { roomIds, enabled = true } = options;\n\n const [roomsUsers, setRoomsUsers] = useState<Record<string, PresenceUser[]>>(\n {},\n );\n\n // Serialize roomIds to prevent infinite effect loops when array reference changes\n const roomIdsKey = JSON.stringify(roomIds ?? []);\n const normalizedRoomIds = useMemo(() => {\n const parsed = JSON.parse(roomIdsKey) as string[];\n const uniq = Array.from(new Set(parsed.filter(Boolean)));\n return uniq;\n }, [roomIdsKey]);\n\n const watchedRef = useRef<Set<string>>(new Set());\n\n // 통합된 useEffect: watch/unwatch + socket 이벤트 처리\n useEffect(() => {\n if (!enabled) {\n setRoomsUsers({});\n watchedRef.current = new Set();\n return;\n }\n\n if (!isPresenceSocketInitialized()) {\n console.warn(\n \"[presence-watch] Socket not initialized. Watch features disabled.\",\n );\n return;\n }\n\n const socket = getPresenceSocket();\n\n // Watch/unwatch 로직\n const toWatch = normalizedRoomIds.filter((r) => !watchedRef.current.has(r));\n const toUnwatch = Array.from(watchedRef.current).filter(\n (r) => !normalizedRoomIds.includes(r),\n );\n\n if (toWatch.length > 0) {\n socket.emit(\"presence:watch\", { roomIds: toWatch });\n for (const r of toWatch) watchedRef.current.add(r);\n }\n\n if (toUnwatch.length > 0) {\n socket.emit(\"presence:unwatch\", { roomIds: toUnwatch });\n for (const r of toUnwatch) watchedRef.current.delete(r);\n setRoomsUsers((prev) => {\n const next = { ...prev };\n for (const r of toUnwatch) delete next[r];\n return next;\n });\n }\n\n // 이벤트 핸들러\n const handleSnapshot = (payload: SnapshotPayload) => {\n const rooms = payload?.rooms ?? {};\n setRoomsUsers((prev) => {\n const next = { ...prev };\n for (const [roomId, users] of Object.entries(rooms)) {\n next[roomId] = normalizeUsers(users);\n }\n return next;\n });\n };\n\n const handleUpdate = (payload: unknown) => {\n if (!payload || typeof payload !== \"object\") return;\n const { roomId, users } = payload as UpdatePayload;\n if (!roomId) return;\n if (!watchedRef.current.has(roomId)) return;\n setRoomsUsers((prev) => ({ ...prev, [roomId]: normalizeUsers(users) }));\n };\n\n // Re-watch on reconnect\n const handleConnect = () => {\n const ids = Array.from(watchedRef.current);\n if (ids.length > 0) socket.emit(\"presence:watch\", { roomIds: ids });\n };\n\n socket.on(\"presence:snapshot\", handleSnapshot);\n socket.on(\"presence:update\", handleUpdate);\n socket.on(\"connect\", handleConnect);\n\n return () => {\n socket.off(\"presence:snapshot\", handleSnapshot);\n socket.off(\"presence:update\", handleUpdate);\n socket.off(\"connect\", handleConnect);\n\n // cleanup 시 현재 normalizedRoomIds 기준으로 unwatch\n if (normalizedRoomIds.length > 0) {\n socket.emit(\"presence:unwatch\", { roomIds: normalizedRoomIds });\n }\n watchedRef.current = new Set();\n };\n }, [enabled, normalizedRoomIds]);\n\n return roomsUsers;\n}\n","import { useMemo, useState, useCallback } from \"react\";\nimport type {\n PresenceUser,\n UsePresenceAvatarsStateOptions,\n UsePresenceAvatarsStateReturn,\n} from \"../core/types\";\nimport { getInitial } from \"../core/utils\";\n\n/**\n * Headless hook for presence avatars UI state management\n *\n * This hook provides all the state and logic needed to build\n * a presence avatars component without any styling.\n *\n * @param options - Configuration options\n * @returns State and handlers for building presence avatars UI\n *\n * @example\n * ```tsx\n * const {\n * visibleUsers,\n * moreCount,\n * hoveredUser,\n * setHoveredIndex,\n * getInitial,\n * getZIndex,\n * } = usePresenceAvatarsState({ users, maxVisible: 3 });\n *\n * return (\n * <div>\n * {visibleUsers.map((user, idx) => (\n * <span\n * key={user.userId}\n * style={{ zIndex: getZIndex(idx, hoveredUser?.userId === user.userId) }}\n * onMouseEnter={() => setHoveredIndex(idx)}\n * onMouseLeave={() => setHoveredIndex(null)}\n * >\n * {getInitial(user)}\n * </span>\n * ))}\n * {moreCount > 0 && <span>+{moreCount}</span>}\n * </div>\n * );\n * ```\n */\nexport function usePresenceAvatarsState<T extends PresenceUser = PresenceUser>(\n options: UsePresenceAvatarsStateOptions<T>,\n): UsePresenceAvatarsStateReturn<T> {\n const { users, maxVisible = 3 } = options;\n\n const safeUsers = Array.isArray(users) ? users : [];\n\n const visibleUsers = useMemo(\n () => safeUsers.slice(0, maxVisible),\n [safeUsers, maxVisible],\n );\n\n const moreCount = Math.max(0, safeUsers.length - visibleUsers.length);\n\n const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);\n\n const hoveredUser = hoveredIndex != null ? visibleUsers[hoveredIndex] : null;\n\n const baseTopZ = visibleUsers.length + 1;\n\n const getZIndex = useCallback(\n (index: number, isHovered: boolean): number => {\n return isHovered ? 100 : baseTopZ - index;\n },\n [baseTopZ],\n );\n\n return {\n visibleUsers,\n moreCount,\n hoveredUser,\n hoveredIndex,\n setHoveredIndex,\n getInitial,\n getZIndex,\n };\n}\n","import type { PresenceUser } from \"./types\";\n\n/**\n * Extract initial character from user name/id\n * @param user - PresenceUser object\n * @returns Single character initial (uppercase for letters)\n */\nexport function getInitial(user: PresenceUser): string {\n const base = (user?.name || user?.userId || \"\").trim();\n if (!base) return \"?\";\n const ch = base.charAt(0);\n return /[a-z]/i.test(ch) ? ch.toUpperCase() : ch;\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport type {\n PresenceUser,\n UsePresenceFloatingStateOptions,\n UsePresenceFloatingStateReturn,\n} from \"../core/types\";\n\n/**\n * Headless hook for presence floating UI state management\n *\n * This hook provides all the state and logic needed to build\n * a draggable floating presence panel without any styling.\n *\n * @param options - Configuration options\n * @returns State and handlers for building floating presence UI\n *\n * @example\n * ```tsx\n * const {\n * containerRef,\n * inlineStyle,\n * isDragging,\n * visibleUsers,\n * moreCount,\n * hoveredUser,\n * tooltipTop,\n * onMouseDownHeader,\n * onAvatarEnter,\n * onAvatarLeave,\n * } = usePresenceFloatingState({ users, maxVisible: 8 });\n *\n * return (\n * <div ref={containerRef} style={inlineStyle} onMouseDown={onMouseDownHeader}>\n * <div className=\"header\">열람중 {users.length}</div>\n * <div className=\"body\">\n * {visibleUsers.map((user) => (\n * <span\n * key={user.userId}\n * onMouseEnter={(e) => onAvatarEnter(e, user)}\n * onMouseLeave={onAvatarLeave}\n * >\n * {user.name}\n * </span>\n * ))}\n * </div>\n * </div>\n * );\n * ```\n */\nexport function usePresenceFloatingState<T extends PresenceUser = PresenceUser>(\n options: UsePresenceFloatingStateOptions<T>,\n): UsePresenceFloatingStateReturn<T> {\n const { users, maxVisible = 8, initialPosition = { top: 166 } } = options;\n\n const containerRef = useRef<HTMLDivElement>(null);\n const [dragging, setDragging] = useState(false);\n const [hasDragged, setHasDragged] = useState(false);\n const [dragOffset, setDragOffset] = useState<{ x: number; y: number }>({\n x: 0,\n y: 0,\n });\n const [position, setPosition] = useState<{ top: number; left: number }>({\n top: initialPosition.top,\n left: 0,\n });\n const [hoveredUser, setHoveredUser] = useState<T | null>(null);\n const [tooltipTop, setTooltipTop] = useState<number>(0);\n\n const safeUsers = Array.isArray(users) ? users : [];\n const visibleUsers = safeUsers.slice(0, maxVisible);\n const moreCount = Math.max(0, safeUsers.length - visibleUsers.length);\n\n // Position adjustment after drag (resize handling)\n useEffect(() => {\n if (!hasDragged) return;\n\n const el = containerRef.current;\n if (!el) return;\n\n const adjustPosition = () => {\n const rect = el.getBoundingClientRect();\n const maxLeft = window.innerWidth - rect.width;\n const maxTop = window.innerHeight - rect.height - 8;\n setPosition((prev) => ({\n left: Math.min(Math.max(0, prev.left), Math.max(0, maxLeft)),\n top: Math.min(Math.max(8, prev.top), Math.max(8, maxTop)),\n }));\n };\n\n const resizeObserver = new ResizeObserver(() => {\n adjustPosition();\n });\n resizeObserver.observe(el);\n\n window.addEventListener(\"resize\", adjustPosition);\n\n return () => {\n resizeObserver.disconnect();\n window.removeEventListener(\"resize\", adjustPosition);\n };\n }, [hasDragged]);\n\n // Helper to get coordinates from mouse or touch event\n const getEventCoordinates = useCallback(\n (e: MouseEvent | TouchEvent | React.MouseEvent | React.TouchEvent) => {\n if (\"touches\" in e && e.touches.length > 0) {\n return { clientX: e.touches[0].clientX, clientY: e.touches[0].clientY };\n }\n if (\"changedTouches\" in e && e.changedTouches.length > 0) {\n return {\n clientX: e.changedTouches[0].clientX,\n clientY: e.changedTouches[0].clientY,\n };\n }\n if (\"clientX\" in e) {\n return { clientX: e.clientX, clientY: e.clientY };\n }\n return { clientX: 0, clientY: 0 };\n },\n [],\n );\n\n const onMouseDownHeader = useCallback(\n (e: React.MouseEvent) => {\n const el = containerRef.current;\n if (!el) return;\n const rect = el.getBoundingClientRect();\n\n // On first drag, switch to left-based positioning\n if (!hasDragged) {\n setHasDragged(true);\n setPosition({\n top: rect.top,\n left: rect.left,\n });\n }\n\n setDragOffset({ x: e.clientX - rect.left, y: e.clientY - rect.top });\n setDragging(true);\n e.preventDefault();\n },\n [hasDragged],\n );\n\n const onTouchStartHeader = useCallback(\n (e: React.TouchEvent) => {\n const el = containerRef.current;\n if (!el) return;\n const rect = el.getBoundingClientRect();\n const { clientX, clientY } = getEventCoordinates(e);\n\n // On first drag, switch to left-based positioning\n if (!hasDragged) {\n setHasDragged(true);\n setPosition({\n top: rect.top,\n left: rect.left,\n });\n }\n\n setDragOffset({ x: clientX - rect.left, y: clientY - rect.top });\n setDragging(true);\n // Note: Don't prevent default to allow scrolling detection\n },\n [hasDragged, getEventCoordinates],\n );\n\n // Handle drag movement (mouse and touch)\n useEffect(() => {\n if (!dragging) return;\n\n const el = containerRef.current;\n\n const onMove = (e: MouseEvent | TouchEvent) => {\n if (!el) return;\n const rect = el.getBoundingClientRect();\n const { clientX, clientY } = getEventCoordinates(e);\n const newLeft = clientX - dragOffset.x;\n const newTop = clientY - dragOffset.y;\n const maxLeft = window.innerWidth - rect.width;\n const maxTop = window.innerHeight - rect.height - 8;\n setPosition({\n left: Math.min(Math.max(0, newLeft), Math.max(0, maxLeft)),\n top: Math.min(Math.max(8, newTop), Math.max(8, maxTop)),\n });\n\n // Prevent scrolling while dragging on touch\n if (\"touches\" in e) {\n e.preventDefault();\n }\n };\n\n const onUp = () => setDragging(false);\n\n // Mouse events\n window.addEventListener(\"mousemove\", onMove);\n window.addEventListener(\"mouseup\", onUp, { once: true });\n\n // Touch events\n window.addEventListener(\"touchmove\", onMove, { passive: false });\n window.addEventListener(\"touchend\", onUp, { once: true });\n window.addEventListener(\"touchcancel\", onUp, { once: true });\n\n return () => {\n window.removeEventListener(\"mousemove\", onMove);\n window.removeEventListener(\"mouseup\", onUp);\n window.removeEventListener(\"touchmove\", onMove);\n window.removeEventListener(\"touchend\", onUp);\n window.removeEventListener(\"touchcancel\", onUp);\n };\n }, [dragging, dragOffset.x, dragOffset.y, getEventCoordinates]);\n\n const inlineStyle = useMemo<React.CSSProperties>(() => {\n if (hasDragged) {\n return {\n position: \"fixed\",\n top: position.top,\n left: position.left,\n right: \"auto\",\n };\n } else {\n return {\n position: \"fixed\",\n top: position.top,\n right: 0,\n left: \"auto\",\n };\n }\n }, [position.top, position.left, hasDragged]);\n\n const onAvatarEnter = useCallback((e: React.MouseEvent, user: T) => {\n const container = containerRef.current;\n if (!container) {\n setHoveredUser(user);\n setTooltipTop(0);\n return;\n }\n const containerRect = container.getBoundingClientRect();\n const targetRect = (e.currentTarget as HTMLElement).getBoundingClientRect();\n const topWithinContainer = targetRect.top - containerRect.top;\n setHoveredUser(user);\n setTooltipTop(topWithinContainer);\n }, []);\n\n const onAvatarLeave = useCallback(() => {\n setHoveredUser(null);\n }, []);\n\n return {\n containerRef,\n position,\n isDragging: dragging,\n hasDragged,\n inlineStyle,\n visibleUsers,\n moreCount,\n hoveredUser,\n tooltipTop,\n onMouseDownHeader,\n onTouchStartHeader,\n onAvatarEnter,\n onAvatarLeave,\n };\n}\n","import React, { useState, useCallback } from \"react\";\nimport { usePresenceAvatarsState } from \"../headless/usePresenceAvatarsState\";\nimport type {\n PresenceUser,\n PresenceAvatarsProps,\n PresenceAvatarRenderProps,\n PresenceAvatarsTooltipRenderProps,\n} from \"../core/types\";\n\n/**\n * PresenceAvatars - Displays a stack of user avatars with hover tooltips\n *\n * @example\n * ```tsx\n * // Basic usage\n * <PresenceAvatars users={users} />\n *\n * // Custom avatar\n * <PresenceAvatars\n * users={users}\n * renderAvatar={({ user, initial }) => (\n * <img src={user.avatarUrl} alt={user.name} />\n * )}\n * />\n *\n * // Custom tooltip\n * <PresenceAvatars\n * users={users}\n * renderTooltip={({ user, position }) => (\n * <div style={{ top: position.top, left: position.left }}>\n * {user.name}\n * </div>\n * )}\n * />\n * ```\n */\nexport function PresenceAvatars<T extends PresenceUser = PresenceUser>({\n users,\n maxVisible = 3,\n className,\n renderAvatar,\n renderTooltip,\n renderMore,\n}: PresenceAvatarsProps<T>) {\n const {\n visibleUsers,\n moreCount,\n hoveredUser,\n hoveredIndex,\n setHoveredIndex,\n getInitial,\n getZIndex,\n } = usePresenceAvatarsState<T>({ users, maxVisible });\n\n const [tooltipPos, setTooltipPos] = useState<{\n top: number;\n left: number;\n } | null>(null);\n\n const updateTooltipPos = useCallback((el: HTMLElement) => {\n const rect = el.getBoundingClientRect();\n const tooltipWidth = 200;\n const half = tooltipWidth / 2;\n const centerX = rect.left + rect.width / 2;\n const left = Math.min(\n Math.max(centerX, half + 8),\n window.innerWidth - half - 8,\n );\n const top = Math.min(rect.bottom + 8, window.innerHeight - 8);\n setTooltipPos({ top, left });\n }, []);\n\n const handleMouseEnter = useCallback(\n (e: React.MouseEvent, idx: number) => {\n setHoveredIndex(idx);\n updateTooltipPos(e.currentTarget as HTMLElement);\n },\n [setHoveredIndex, updateTooltipPos],\n );\n\n const handleMouseLeave = useCallback(() => {\n setHoveredIndex(null);\n }, [setHoveredIndex]);\n\n // Default tooltip renderer\n const defaultTooltipRenderer = useCallback(\n ({ user, position }: PresenceAvatarsTooltipRenderProps) => (\n <div\n className=\"presence-avatars__tooltip\"\n style={{ top: position.top, left: position.left }}\n >\n <div className=\"presence-avatars__tooltip-row\">\n <span className=\"presence-avatars__tooltip-key\">Name</span>\n <span className=\"presence-avatars__tooltip-val\">\n {user.name || \"-\"}\n </span>\n </div>\n <div className=\"presence-avatars__tooltip-row\">\n <span className=\"presence-avatars__tooltip-key\">ID</span>\n <span className=\"presence-avatars__tooltip-val\">\n {user.userId || \"-\"}\n </span>\n </div>\n </div>\n ),\n [],\n );\n\n // Default avatar renderer\n const defaultAvatarRenderer = useCallback(\n ({ initial, isHovered }: PresenceAvatarRenderProps) => <>{initial}</>,\n [],\n );\n\n return (\n <div className={`presence-avatars ${className ?? \"\"}`}>\n <div className=\"presence-avatars__stack\">\n {visibleUsers.map((user, idx) => {\n const isHovered = hoveredIndex === idx;\n const initial = getInitial(user);\n const zIndex = getZIndex(idx, isHovered);\n\n const renderProps: PresenceAvatarRenderProps<T> = {\n user,\n index: idx,\n initial,\n isHovered,\n zIndex,\n };\n\n return (\n <span\n key={`${user.userId}-${idx}`}\n className={`presence-avatars__avatar ${\n isHovered ? \"is-hovered\" : \"\"\n }`}\n style={{ zIndex }}\n onMouseEnter={(e) => handleMouseEnter(e, idx)}\n onMouseLeave={handleMouseLeave}\n aria-label={user.name || user.userId || \"\"}\n >\n {renderAvatar\n ? renderAvatar(renderProps)\n : defaultAvatarRenderer(renderProps)}\n </span>\n );\n })}\n {moreCount > 0 &&\n (renderMore ? (\n renderMore(moreCount)\n ) : (\n <span className=\"presence-avatars__more\">+{moreCount}</span>\n ))}\n </div>\n\n {hoveredUser &&\n tooltipPos &&\n (renderTooltip\n ? renderTooltip({ user: hoveredUser, position: tooltipPos })\n : defaultTooltipRenderer({\n user: hoveredUser,\n position: tooltipPos,\n }))}\n </div>\n );\n}\n\nexport default PresenceAvatars;\n","import React, { useCallback } from \"react\";\nimport { usePresenceFloatingState } from \"../headless/usePresenceFloatingState\";\nimport type {\n PresenceUser,\n PresenceFloatingProps,\n PresenceFloatingAvatarRenderProps,\n PresenceFloatingTooltipRenderProps,\n} from \"../core/types\";\nimport { getInitial } from \"../core/utils\";\n\n/**\n * PresenceFloating - Draggable floating panel showing presence users\n *\n * @example\n * ```tsx\n * // Basic usage\n * <PresenceFloating users={users} />\n *\n * // Custom text\n * <PresenceFloating\n * users={users}\n * title=\"Viewing\"\n * emptyText=\"No one\"\n * moreText={(n) => `+${n} more`}\n * />\n *\n * // Custom avatar\n * <PresenceFloating\n * users={users}\n * renderAvatar={({ user, initial }) => (\n * <img src={user.avatarUrl} alt={user.name} />\n * )}\n * />\n * ```\n */\nexport function PresenceFloating<T extends PresenceUser = PresenceUser>({\n users,\n maxVisible = 8,\n className,\n style,\n initialPosition,\n title = \"열람중\",\n emptyText = \"없음\",\n moreText = (n) => `+${n}명`,\n renderAvatar,\n renderTooltip,\n}: PresenceFloatingProps<T>) {\n const {\n containerRef,\n inlineStyle,\n isDragging,\n visibleUsers,\n moreCount,\n hoveredUser,\n tooltipTop,\n onMouseDownHeader,\n onTouchStartHeader,\n onAvatarEnter,\n onAvatarLeave,\n } = usePresenceFloatingState<T>({ users, maxVisible, initialPosition });\n\n // Default tooltip renderer\n const defaultTooltipRenderer = useCallback(\n ({ user, top }: PresenceFloatingTooltipRenderProps) => (\n <div\n className=\"presence-floating__tooltip\"\n style={{ top: Math.max(0, top - 6) }}\n onMouseLeave={onAvatarLeave}\n >\n <div className=\"presence-floating__tooltip-row\">\n <span className=\"presence-floating__tooltip-key\">Name</span>\n <span className=\"presence-floating__tooltip-val\">\n {user.name || \"-\"}\n </span>\n </div>\n <div className=\"presence-floating__tooltip-row\">\n <span className=\"presence-floating__tooltip-key\">ID</span>\n <span className=\"presence-floating__tooltip-val\">\n {user.userId || \"-\"}\n </span>\n </div>\n </div>\n ),\n [onAvatarLeave],\n );\n\n // Default avatar renderer\n const defaultAvatarRenderer = useCallback(\n ({ initial }: PresenceFloatingAvatarRenderProps) => <>{initial}</>,\n [],\n );\n\n const combinedStyle: React.CSSProperties = {\n ...inlineStyle,\n ...style,\n };\n\n return (\n <div\n ref={containerRef}\n className={`presence-floating ${isDragging ? \"is-dragging\" : \"\"} ${\n className ?? \"\"\n }`}\n style={combinedStyle}\n >\n <div\n className=\"presence-floating__header\"\n onMouseDown={onMouseDownHeader}\n onTouchStart={onTouchStartHeader}\n >\n <span\n className=\"presence-floating__title\"\n title={title}\n aria-label={title}\n >\n {title}\n </span>\n <span className=\"presence-floating__count\">{users.length}</span>\n </div>\n\n {hoveredUser &&\n (renderTooltip\n ? renderTooltip({ user: hoveredUser, top: tooltipTop })\n : defaultTooltipRenderer({ user: hoveredUser, top: tooltipTop }))}\n\n <div className=\"presence-floating__body\">\n {visibleUsers.length === 0 ? (\n <span className=\"presence-floating__empty\">{emptyText}</span>\n ) : (\n visibleUsers.map((user, idx) => {\n const initial = getInitial(user);\n const label = user.name || user.userId || \"\";\n\n const renderProps: PresenceFloatingAvatarRenderProps<T> = {\n user,\n initial,\n };\n\n return (\n <span\n key={`${user.userId}-${idx}`}\n className=\"presence-floating__avatar\"\n title={label}\n aria-label={label}\n onMouseEnter={(e) => onAvatarEnter(e, user)}\n onMouseLeave={onAvatarLeave}\n >\n {renderAvatar\n ? renderAvatar(renderProps)\n : defaultAvatarRenderer(renderProps)}\n </span>\n );\n })\n )}\n {moreCount > 0 && (\n <span className=\"presence-floating__more\">{moreText(moreCount)}</span>\n )}\n </div>\n </div>\n );\n}\n\nexport default PresenceFloating;\n","import React, { createContext, useContext, useEffect, useState } from \"react\";\nimport {\n initPresenceSocket,\n disconnectPresenceSocket,\n isPresenceSocketInitialized,\n getPresenceSocket,\n} from \"../core/socket\";\nimport type { PresenceProviderProps } from \"../core/types\";\n\n/**\n * Presence Context value type\n */\ntype PresenceContextValue = {\n /** Whether the socket is initialized */\n isInitialized: boolean;\n /** Current connection status */\n status: \"connecting\" | \"connected\" | \"disconnected\";\n};\n\nconst PresenceContext = createContext<PresenceContextValue | null>(null);\n\n/**\n * PresenceProvider - Manages socket initialization lifecycle\n *\n * Wrap your app with this provider to automatically initialize\n * and cleanup the presence socket connection.\n *\n * @example\n * ```tsx\n * // In your app entry point\n * import { PresenceProvider } from '@rencar-dev/feature-modules-public';\n *\n * function App() {\n * return (\n * <PresenceProvider url=\"https://socket.example.com\">\n * <MyApp />\n * </PresenceProvider>\n * );\n * }\n *\n * // In child components, hooks work automatically\n * function MyComponent() {\n * const users = usePresence({ roomId: 'room-1', currentUser });\n * return <PresenceFloating users={users} />;\n * }\n * ```\n */\nexport function PresenceProvider({\n url,\n transports,\n debug = false,\n children,\n}: PresenceProviderProps) {\n const [status, setStatus] =\n useState<PresenceContextValue[\"status\"]>(\"disconnected\");\n const [isInitialized, setIsInitialized] = useState(false);\n\n useEffect(() => {\n // Initialize socket\n initPresenceSocket({ url, transports });\n setIsInitialized(true);\n\n if (debug) {\n console.log(\"[presence] Socket initialized with URL:\", url);\n }\n\n // Get socket and setup connection listeners\n const socket = getPresenceSocket();\n\n const handleConnect = () => {\n setStatus(\"connected\");\n if (debug) {\n console.log(\"[presence] Connected\");\n }\n };\n\n const handleDisconnect = () => {\n setStatus(\"disconnected\");\n if (debug) {\n console.log(\"[presence] Disconnected\");\n }\n };\n\n const handleConnecting = () => {\n setStatus(\"connecting\");\n };\n\n // Set initial status\n if (socket.connected) {\n setStatus(\"connected\");\n } else {\n setStatus(\"connecting\");\n }\n\n socket.on(\"connect\", handleConnect);\n socket.on(\"disconnect\", handleDisconnect);\n socket.io.on(\"reconnect_attempt\", handleConnecting);\n\n return () => {\n socket.off(\"connect\", handleConnect);\n socket.off(\"disconnect\", handleDisconnect);\n socket.io.off(\"reconnect_attempt\", handleConnecting);\n\n disconnectPresenceSocket();\n setIsInitialized(false);\n\n if (debug) {\n console.log(\"[presence] Socket disconnected and cleaned up\");\n }\n };\n }, [url, transports, debug]);\n\n const contextValue: PresenceContextValue = {\n isInitialized,\n status,\n };\n\n return (\n <PresenceContext.Provider value={contextValue}>\n {children}\n </PresenceContext.Provider>\n );\n}\n\n/**\n * Hook to access presence connection status\n *\n * @returns Connection status object or null if not inside PresenceProvider\n *\n * @example\n * ```tsx\n * function StatusIndicator() {\n * const presence = usePresenceStatus();\n *\n * if (!presence) {\n * return <span>Not initialized</span>;\n * }\n *\n * return (\n * <span>\n * Status: {presence.status}\n * {presence.status === 'connecting' && <Spinner />}\n * </span>\n * );\n * }\n * ```\n */\nexport function usePresenceStatus(): PresenceContextValue | null {\n return useContext(PresenceContext);\n}\n\nexport default PresenceProvider;\n"],"mappings":"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","names":["normalizeUsers","import_react","import_react","import_react","import_react","getInitial","import_react","import_jsx_runtime","import_react","import_jsx_runtime"]}