@rencar-dev/feature-modules-public 0.0.7 → 1.1.0
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/README.md +65 -170
- package/dist/presence/index.cjs +230 -2
- package/dist/presence/index.cjs.map +1 -1
- package/dist/presence/index.d.cts +136 -1
- package/dist/presence/index.d.ts +136 -1
- package/dist/presence/index.js +228 -2
- package/dist/presence/index.js.map +1 -1
- package/dist/presence/styles.css +240 -0
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -10,206 +10,101 @@ npm install @rencar-dev/feature-modules-public
|
|
|
10
10
|
yarn add @rencar-dev/feature-modules-public
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## 모듈 목록
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
function MyComponent() {
|
|
20
|
-
const { count, increment, decrement, reset } = useExample(0);
|
|
21
|
-
|
|
22
|
-
return (
|
|
23
|
-
<div>
|
|
24
|
-
<p>Count: {count}</p>
|
|
25
|
-
<button onClick={increment}>+</button>
|
|
26
|
-
<button onClick={decrement}>-</button>
|
|
27
|
-
<button onClick={reset}>Reset</button>
|
|
28
|
-
</div>
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## Presence 모듈
|
|
34
|
-
|
|
35
|
-
실시간 사용자 프레즌스(현재 접속자) 기능을 위한 모듈입니다.
|
|
36
|
-
|
|
37
|
-
### Socket 초기화
|
|
38
|
-
|
|
39
|
-
프레즌스 훅을 사용하기 전에 반드시 소켓을 초기화해야 합니다.
|
|
40
|
-
|
|
41
|
-
#### initPresenceSocket
|
|
42
|
-
|
|
43
|
-
소켓 연결을 설정합니다. **앱 시작 시 1회 호출**해야 합니다.
|
|
44
|
-
|
|
45
|
-
```tsx
|
|
46
|
-
import { initPresenceSocket } from "@rencar-dev/feature-modules-public/presence";
|
|
47
|
-
|
|
48
|
-
// 앱 초기화 시 (예: App.tsx 또는 index.tsx)
|
|
49
|
-
initPresenceSocket({
|
|
50
|
-
url: "https://your-socket-server.com",
|
|
51
|
-
transports: ["websocket"], // 선택사항, 기본값: ["websocket"]
|
|
52
|
-
});
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
**PresenceConfig:**
|
|
56
|
-
|
|
57
|
-
| 옵션 | 타입 | 필수 | 기본값 | 설명 |
|
|
58
|
-
| ------------ | ------------------------------ | ---- | --------------- | ------------- |
|
|
59
|
-
| `url` | `string` | ✅ | - | 소켓 서버 URL |
|
|
60
|
-
| `transports` | `("websocket" \| "polling")[]` | - | `["websocket"]` | 전송 방식 |
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
#### getPresenceSocket
|
|
65
|
-
|
|
66
|
-
공유 소켓 인스턴스를 반환합니다. 초기화되지 않은 경우 에러를 발생시킵니다.
|
|
67
|
-
|
|
68
|
-
```tsx
|
|
69
|
-
import { getPresenceSocket } from "@rencar-dev/feature-modules-public/presence";
|
|
70
|
-
|
|
71
|
-
const socket = getPresenceSocket();
|
|
72
|
-
socket.emit("custom-event", { data: "example" });
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
---
|
|
76
|
-
|
|
77
|
-
#### disconnectPresenceSocket
|
|
78
|
-
|
|
79
|
-
소켓 연결을 종료하고 정리합니다. 앱 종료 시 또는 로그아웃 시 호출할 수 있습니다.
|
|
15
|
+
| 모듈 | 설명 | 문서 |
|
|
16
|
+
| -------- | ---------------------- | ------------------------------------ |
|
|
17
|
+
| hooks | 유틸리티 React 훅 | [docs/hooks.md](docs/hooks.md) |
|
|
18
|
+
| presence | 실시간 접속자 프레즌스 | [docs/presence.md](docs/presence.md) |
|
|
80
19
|
|
|
81
20
|
```tsx
|
|
82
|
-
|
|
21
|
+
// hooks 모듈 import
|
|
22
|
+
import { useExample } from "@rencar-dev/feature-modules-public/hooks";
|
|
83
23
|
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
24
|
+
// presence 모듈 import
|
|
25
|
+
import {
|
|
26
|
+
usePresence,
|
|
27
|
+
PresenceFloating,
|
|
28
|
+
} from "@rencar-dev/feature-modules-public/presence";
|
|
29
|
+
import "@rencar-dev/feature-modules-public/presence/styles.css";
|
|
89
30
|
```
|
|
90
31
|
|
|
91
32
|
---
|
|
92
33
|
|
|
93
|
-
|
|
34
|
+
## 개발 가이드
|
|
94
35
|
|
|
95
|
-
|
|
36
|
+
### 로컬 개발
|
|
96
37
|
|
|
97
|
-
```
|
|
98
|
-
|
|
38
|
+
```bash
|
|
39
|
+
# 개발 모드 (watch)
|
|
40
|
+
npm run dev
|
|
99
41
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
42
|
+
# 빌드
|
|
43
|
+
npm run build
|
|
103
44
|
```
|
|
104
45
|
|
|
105
|
-
|
|
46
|
+
### 새 모듈 추가하기
|
|
106
47
|
|
|
107
|
-
|
|
48
|
+
1. **폴더 생성**: `src/[모듈명]/` 폴더 생성
|
|
49
|
+
2. **Entry 파일**: `src/[모듈명]/index.ts` 생성 및 export 정의
|
|
50
|
+
3. **tsup 설정**: `tsup.config.ts`에 entry 추가
|
|
108
51
|
|
|
109
|
-
|
|
52
|
+
```ts
|
|
53
|
+
entry: {
|
|
54
|
+
"hooks/index": "src/hooks/index.ts",
|
|
55
|
+
"presence/index": "src/presence/index.ts",
|
|
56
|
+
"[모듈명]/index": "src/[모듈명]/index.ts", // 추가
|
|
57
|
+
},
|
|
58
|
+
```
|
|
110
59
|
|
|
111
|
-
|
|
60
|
+
4. **package.json exports 추가**:
|
|
112
61
|
|
|
113
|
-
|
|
62
|
+
```json
|
|
63
|
+
"./[모듈명]": {
|
|
64
|
+
"types": "./dist/[모듈명]/index.d.ts",
|
|
65
|
+
"import": "./dist/[모듈명]/index.js",
|
|
66
|
+
"require": "./dist/[모듈명]/index.cjs"
|
|
67
|
+
}
|
|
68
|
+
```
|
|
114
69
|
|
|
115
|
-
**
|
|
70
|
+
5. **typesVersions 추가**:
|
|
116
71
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
| `currentUser` | `PresenceUser` | ✅ | - | 현재 사용자 정보 (`{ userId, name, ...추가속성 }`) |
|
|
121
|
-
| `enabled` | `boolean` | - | `true` | 훅 활성화 여부 |
|
|
122
|
-
| `heartbeatInterval` | `number` | - | `600000` (10분) | 하트비트 간격 (ms) |
|
|
72
|
+
```json
|
|
73
|
+
"[모듈명]": ["./dist/[모듈명]/index.d.ts"]
|
|
74
|
+
```
|
|
123
75
|
|
|
124
|
-
|
|
76
|
+
6. **문서 작성**: `docs/[모듈명].md` 생성
|
|
77
|
+
7. **README 업데이트**: 모듈 목록 테이블에 추가
|
|
125
78
|
|
|
126
|
-
|
|
79
|
+
### 배포
|
|
127
80
|
|
|
128
|
-
|
|
129
|
-
import { usePresence } from "@rencar-dev/feature-modules-public/presence";
|
|
130
|
-
|
|
131
|
-
function MyPage() {
|
|
132
|
-
const users = usePresence({
|
|
133
|
-
roomId: "my-app:page-123",
|
|
134
|
-
currentUser: { userId: "1", name: "John Doe" },
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
return (
|
|
138
|
-
<div>
|
|
139
|
-
<p>현재 접속자: {users.length}명</p>
|
|
140
|
-
<ul>
|
|
141
|
-
{users.map((user) => (
|
|
142
|
-
<li key={user.userId}>{user.name}</li>
|
|
143
|
-
))}
|
|
144
|
-
</ul>
|
|
145
|
-
</div>
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
```
|
|
81
|
+
**자동 배포 (권장):**
|
|
149
82
|
|
|
150
|
-
|
|
83
|
+
`master` 브랜치에 push하면 semantic-release가 자동으로:
|
|
151
84
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
enabled: isLoggedIn,
|
|
158
|
-
});
|
|
159
|
-
```
|
|
85
|
+
1. 커밋 메시지 분석 (`fix:`, `feat:` 등)
|
|
86
|
+
2. 버전 번호 결정 및 package.json 업데이트
|
|
87
|
+
3. Git 태그 생성
|
|
88
|
+
4. npm 배포
|
|
89
|
+
5. GitHub Release 생성
|
|
160
90
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
91
|
+
| 커밋 접두사 | 버전 변경 | 예시 |
|
|
92
|
+
| ----------- | ------------- | ------------------------ |
|
|
93
|
+
| `fix:` | patch (0.0.X) | `fix: 버그 수정` |
|
|
94
|
+
| `feat:` | minor (0.X.0) | `feat: 새 기능 추가` |
|
|
95
|
+
| `feat!:` | major (X.0.0) | `feat!: breaking change` |
|
|
166
96
|
|
|
167
|
-
**
|
|
97
|
+
> **Note**: `chore:`, `docs:`, `style:` 등은 릴리즈를 트리거하지 않습니다.
|
|
168
98
|
|
|
169
|
-
|
|
170
|
-
| --------- | ---------- | ---- | ------ | ----------------- |
|
|
171
|
-
| `roomIds` | `string[]` | ✅ | - | 관찰할 룸 ID 배열 |
|
|
172
|
-
| `enabled` | `boolean` | - | `true` | 훅 활성화 여부 |
|
|
99
|
+
**수동 배포:**
|
|
173
100
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
```tsx
|
|
179
|
-
import { usePresenceWatch } from "@rencar-dev/feature-modules-public/presence";
|
|
180
|
-
|
|
181
|
-
function Dashboard() {
|
|
182
|
-
const roomsUsers = usePresenceWatch({
|
|
183
|
-
roomIds: ["page-1", "page-2", "page-3"],
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
return (
|
|
187
|
-
<div>
|
|
188
|
-
{Object.entries(roomsUsers).map(([roomId, users]) => (
|
|
189
|
-
<div key={roomId}>
|
|
190
|
-
<h3>{roomId}</h3>
|
|
191
|
-
<p>{users.length}명 접속 중</p>
|
|
192
|
-
</div>
|
|
193
|
-
))}
|
|
194
|
-
</div>
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
**동적 룸 ID:**
|
|
200
|
-
|
|
201
|
-
```tsx
|
|
202
|
-
// 룸 ID가 변경되면 자동으로 watch/unwatch 처리
|
|
203
|
-
const [selectedRooms, setSelectedRooms] = useState(["room-a"]);
|
|
204
|
-
const roomsUsers = usePresenceWatch({ roomIds: selectedRooms });
|
|
101
|
+
```bash
|
|
102
|
+
npm version patch # 또는 minor, major
|
|
103
|
+
npm publish
|
|
205
104
|
```
|
|
206
105
|
|
|
207
106
|
---
|
|
208
107
|
|
|
209
|
-
|
|
108
|
+
## 라이선스
|
|
210
109
|
|
|
211
|
-
|
|
212
|
-
| ----------- | -------------------------------- | ----------------------------- |
|
|
213
|
-
| **룸 참여** | ✅ 참여함 (다른 사용자에게 보임) | ❌ 참여 안함 (관찰만) |
|
|
214
|
-
| **룸 개수** | 1개 | 여러 개 |
|
|
215
|
-
| **용도** | 현재 페이지 접속자 표시 | 대시보드에서 여러 룸 모니터링 |
|
|
110
|
+
MIT
|
package/dist/presence/index.cjs
CHANGED
|
@@ -20,6 +20,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/presence/index.ts
|
|
21
21
|
var presence_exports = {};
|
|
22
22
|
__export(presence_exports, {
|
|
23
|
+
PresenceAvatars: () => PresenceAvatars,
|
|
24
|
+
PresenceFloating: () => PresenceFloating,
|
|
23
25
|
disconnectPresenceSocket: () => disconnectPresenceSocket,
|
|
24
26
|
getPresenceSocket: () => getPresenceSocket,
|
|
25
27
|
initPresenceSocket: () => initPresenceSocket,
|
|
@@ -243,7 +245,7 @@ function usePresenceAvatarsState(options) {
|
|
|
243
245
|
const [hoveredIndex, setHoveredIndex] = (0, import_react3.useState)(null);
|
|
244
246
|
const hoveredUser = hoveredIndex != null ? visibleUsers[hoveredIndex] : null;
|
|
245
247
|
const baseTopZ = visibleUsers.length + 1;
|
|
246
|
-
const
|
|
248
|
+
const getInitial2 = (0, import_react3.useCallback)((user) => {
|
|
247
249
|
return getInitialFromUser(user);
|
|
248
250
|
}, []);
|
|
249
251
|
const getZIndex = (0, import_react3.useCallback)(
|
|
@@ -258,7 +260,7 @@ function usePresenceAvatarsState(options) {
|
|
|
258
260
|
hoveredUser,
|
|
259
261
|
hoveredIndex,
|
|
260
262
|
setHoveredIndex,
|
|
261
|
-
getInitial,
|
|
263
|
+
getInitial: getInitial2,
|
|
262
264
|
getZIndex
|
|
263
265
|
};
|
|
264
266
|
}
|
|
@@ -445,8 +447,234 @@ function usePresenceFloatingState(options) {
|
|
|
445
447
|
onAvatarLeave
|
|
446
448
|
};
|
|
447
449
|
}
|
|
450
|
+
|
|
451
|
+
// src/presence/components/PresenceAvatars.tsx
|
|
452
|
+
var import_react5 = require("react");
|
|
453
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
454
|
+
function PresenceAvatars({
|
|
455
|
+
users,
|
|
456
|
+
maxVisible = 3,
|
|
457
|
+
className,
|
|
458
|
+
renderAvatar,
|
|
459
|
+
renderTooltip,
|
|
460
|
+
renderMore
|
|
461
|
+
}) {
|
|
462
|
+
const {
|
|
463
|
+
visibleUsers,
|
|
464
|
+
moreCount,
|
|
465
|
+
hoveredUser,
|
|
466
|
+
hoveredIndex,
|
|
467
|
+
setHoveredIndex,
|
|
468
|
+
getInitial: getInitial2,
|
|
469
|
+
getZIndex
|
|
470
|
+
} = usePresenceAvatarsState({ users, maxVisible });
|
|
471
|
+
const [tooltipPos, setTooltipPos] = (0, import_react5.useState)(null);
|
|
472
|
+
const updateTooltipPos = (0, import_react5.useCallback)((el) => {
|
|
473
|
+
const rect = el.getBoundingClientRect();
|
|
474
|
+
const tooltipWidth = 200;
|
|
475
|
+
const half = tooltipWidth / 2;
|
|
476
|
+
const centerX = rect.left + rect.width / 2;
|
|
477
|
+
const left = Math.min(
|
|
478
|
+
Math.max(centerX, half + 8),
|
|
479
|
+
window.innerWidth - half - 8
|
|
480
|
+
);
|
|
481
|
+
const top = Math.min(rect.bottom + 8, window.innerHeight - 8);
|
|
482
|
+
setTooltipPos({ top, left });
|
|
483
|
+
}, []);
|
|
484
|
+
const handleMouseEnter = (0, import_react5.useCallback)(
|
|
485
|
+
(e, idx) => {
|
|
486
|
+
setHoveredIndex(idx);
|
|
487
|
+
updateTooltipPos(e.currentTarget);
|
|
488
|
+
},
|
|
489
|
+
[setHoveredIndex, updateTooltipPos]
|
|
490
|
+
);
|
|
491
|
+
const handleMouseLeave = (0, import_react5.useCallback)(() => {
|
|
492
|
+
setHoveredIndex(null);
|
|
493
|
+
}, [setHoveredIndex]);
|
|
494
|
+
const defaultTooltipRenderer = (0, import_react5.useCallback)(
|
|
495
|
+
({ user, position }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
496
|
+
"div",
|
|
497
|
+
{
|
|
498
|
+
className: "presence-avatars__tooltip",
|
|
499
|
+
style: { top: position.top, left: position.left },
|
|
500
|
+
children: [
|
|
501
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "presence-avatars__tooltip-row", children: [
|
|
502
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "presence-avatars__tooltip-key", children: "Name" }),
|
|
503
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "presence-avatars__tooltip-val", children: user.name || "-" })
|
|
504
|
+
] }),
|
|
505
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "presence-avatars__tooltip-row", children: [
|
|
506
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "presence-avatars__tooltip-key", children: "ID" }),
|
|
507
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "presence-avatars__tooltip-val", children: user.userId || "-" })
|
|
508
|
+
] })
|
|
509
|
+
]
|
|
510
|
+
}
|
|
511
|
+
),
|
|
512
|
+
[]
|
|
513
|
+
);
|
|
514
|
+
const defaultAvatarRenderer = (0, import_react5.useCallback)(
|
|
515
|
+
({ initial, isHovered }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: initial }),
|
|
516
|
+
[]
|
|
517
|
+
);
|
|
518
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: `presence-avatars ${className ?? ""}`, children: [
|
|
519
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "presence-avatars__stack", children: [
|
|
520
|
+
visibleUsers.map((user, idx) => {
|
|
521
|
+
const isHovered = hoveredIndex === idx;
|
|
522
|
+
const initial = getInitial2(user);
|
|
523
|
+
const zIndex = getZIndex(idx, isHovered);
|
|
524
|
+
const renderProps = {
|
|
525
|
+
user,
|
|
526
|
+
index: idx,
|
|
527
|
+
initial,
|
|
528
|
+
isHovered,
|
|
529
|
+
zIndex
|
|
530
|
+
};
|
|
531
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
532
|
+
"span",
|
|
533
|
+
{
|
|
534
|
+
className: `presence-avatars__avatar ${isHovered ? "is-hovered" : ""}`,
|
|
535
|
+
style: { zIndex },
|
|
536
|
+
onMouseEnter: (e) => handleMouseEnter(e, idx),
|
|
537
|
+
onMouseLeave: handleMouseLeave,
|
|
538
|
+
"aria-label": user.name || user.userId || "",
|
|
539
|
+
children: renderAvatar ? renderAvatar(renderProps) : defaultAvatarRenderer(renderProps)
|
|
540
|
+
},
|
|
541
|
+
`${user.userId}-${idx}`
|
|
542
|
+
);
|
|
543
|
+
}),
|
|
544
|
+
moreCount > 0 && (renderMore ? renderMore(moreCount) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "presence-avatars__more", children: [
|
|
545
|
+
"+",
|
|
546
|
+
moreCount
|
|
547
|
+
] }))
|
|
548
|
+
] }),
|
|
549
|
+
hoveredUser && tooltipPos && (renderTooltip ? renderTooltip({ user: hoveredUser, position: tooltipPos }) : defaultTooltipRenderer({
|
|
550
|
+
user: hoveredUser,
|
|
551
|
+
position: tooltipPos
|
|
552
|
+
}))
|
|
553
|
+
] });
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// src/presence/components/PresenceFloating.tsx
|
|
557
|
+
var import_react6 = require("react");
|
|
558
|
+
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
|
+
function PresenceFloating({
|
|
566
|
+
users,
|
|
567
|
+
maxVisible = 8,
|
|
568
|
+
className,
|
|
569
|
+
style,
|
|
570
|
+
initialPosition,
|
|
571
|
+
title = "\uC5F4\uB78C\uC911",
|
|
572
|
+
emptyText = "\uC5C6\uC74C",
|
|
573
|
+
moreText = (n) => `+${n}\uBA85`,
|
|
574
|
+
renderAvatar,
|
|
575
|
+
renderTooltip
|
|
576
|
+
}) {
|
|
577
|
+
const {
|
|
578
|
+
containerRef,
|
|
579
|
+
inlineStyle,
|
|
580
|
+
isDragging,
|
|
581
|
+
visibleUsers,
|
|
582
|
+
moreCount,
|
|
583
|
+
hoveredUser,
|
|
584
|
+
tooltipTop,
|
|
585
|
+
onMouseDownHeader,
|
|
586
|
+
onTouchStartHeader,
|
|
587
|
+
onAvatarEnter,
|
|
588
|
+
onAvatarLeave
|
|
589
|
+
} = usePresenceFloatingState({ users, maxVisible, initialPosition });
|
|
590
|
+
const defaultTooltipRenderer = (0, import_react6.useCallback)(
|
|
591
|
+
({ user, top }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
592
|
+
"div",
|
|
593
|
+
{
|
|
594
|
+
className: "presence-floating__tooltip",
|
|
595
|
+
style: { top: Math.max(0, top - 6) },
|
|
596
|
+
onMouseLeave: onAvatarLeave,
|
|
597
|
+
children: [
|
|
598
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "presence-floating__tooltip-row", children: [
|
|
599
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "presence-floating__tooltip-key", children: "Name" }),
|
|
600
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "presence-floating__tooltip-val", children: user.name || "-" })
|
|
601
|
+
] }),
|
|
602
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "presence-floating__tooltip-row", children: [
|
|
603
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "presence-floating__tooltip-key", children: "ID" }),
|
|
604
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "presence-floating__tooltip-val", children: user.userId || "-" })
|
|
605
|
+
] })
|
|
606
|
+
]
|
|
607
|
+
}
|
|
608
|
+
),
|
|
609
|
+
[onAvatarLeave]
|
|
610
|
+
);
|
|
611
|
+
const defaultAvatarRenderer = (0, import_react6.useCallback)(
|
|
612
|
+
({ initial }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: initial }),
|
|
613
|
+
[]
|
|
614
|
+
);
|
|
615
|
+
const combinedStyle = {
|
|
616
|
+
...inlineStyle,
|
|
617
|
+
...style
|
|
618
|
+
};
|
|
619
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
620
|
+
"div",
|
|
621
|
+
{
|
|
622
|
+
ref: containerRef,
|
|
623
|
+
className: `presence-floating ${isDragging ? "is-dragging" : ""} ${className ?? ""}`,
|
|
624
|
+
style: combinedStyle,
|
|
625
|
+
children: [
|
|
626
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
627
|
+
"div",
|
|
628
|
+
{
|
|
629
|
+
className: "presence-floating__header",
|
|
630
|
+
onMouseDown: onMouseDownHeader,
|
|
631
|
+
onTouchStart: onTouchStartHeader,
|
|
632
|
+
children: [
|
|
633
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
634
|
+
"span",
|
|
635
|
+
{
|
|
636
|
+
className: "presence-floating__title",
|
|
637
|
+
title,
|
|
638
|
+
"aria-label": title,
|
|
639
|
+
children: title
|
|
640
|
+
}
|
|
641
|
+
),
|
|
642
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "presence-floating__count", children: users.length })
|
|
643
|
+
]
|
|
644
|
+
}
|
|
645
|
+
),
|
|
646
|
+
hoveredUser && (renderTooltip ? renderTooltip({ user: hoveredUser, top: tooltipTop }) : defaultTooltipRenderer({ user: hoveredUser, top: tooltipTop })),
|
|
647
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "presence-floating__body", children: [
|
|
648
|
+
visibleUsers.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "presence-floating__empty", children: emptyText }) : visibleUsers.map((user, idx) => {
|
|
649
|
+
const initial = getInitial(user);
|
|
650
|
+
const label = user.name || user.userId || "";
|
|
651
|
+
const renderProps = {
|
|
652
|
+
user,
|
|
653
|
+
initial
|
|
654
|
+
};
|
|
655
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
656
|
+
"span",
|
|
657
|
+
{
|
|
658
|
+
className: "presence-floating__avatar",
|
|
659
|
+
title: label,
|
|
660
|
+
"aria-label": label,
|
|
661
|
+
onMouseEnter: (e) => onAvatarEnter(e, user),
|
|
662
|
+
onMouseLeave: onAvatarLeave,
|
|
663
|
+
children: renderAvatar ? renderAvatar(renderProps) : defaultAvatarRenderer(renderProps)
|
|
664
|
+
},
|
|
665
|
+
`${user.userId}-${idx}`
|
|
666
|
+
);
|
|
667
|
+
}),
|
|
668
|
+
moreCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "presence-floating__more", children: moreText(moreCount) })
|
|
669
|
+
] })
|
|
670
|
+
]
|
|
671
|
+
}
|
|
672
|
+
);
|
|
673
|
+
}
|
|
448
674
|
// Annotate the CommonJS export names for ESM import in node:
|
|
449
675
|
0 && (module.exports = {
|
|
676
|
+
PresenceAvatars,
|
|
677
|
+
PresenceFloating,
|
|
450
678
|
disconnectPresenceSocket,
|
|
451
679
|
getPresenceSocket,
|
|
452
680
|
initPresenceSocket,
|