tycho-components 0.40.9 → 0.41.1

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.
Files changed (28) hide show
  1. package/dist/configs/api/applyAuthErrorHandling.js +2 -3
  2. package/dist/configs/localization/CommentsTexts.d.ts +24 -0
  3. package/dist/configs/localization/CommentsTexts.js +24 -0
  4. package/dist/features/Comments/{CommentAdd.d.ts → CommentAdd/CommentAdd.d.ts} +6 -4
  5. package/dist/features/Comments/CommentAdd/CommentAdd.js +107 -0
  6. package/dist/features/Comments/CommentAdd/index.d.ts +2 -0
  7. package/dist/features/Comments/CommentAdd/index.js +2 -0
  8. package/dist/features/Comments/CommentAdd/style.scss +124 -0
  9. package/dist/features/Comments/CommentInfo.d.ts +1 -0
  10. package/dist/features/Comments/CommentInfo.js +8 -4
  11. package/dist/features/Comments/CommentService.d.ts +9 -8
  12. package/dist/features/Comments/CommentService.js +74 -0
  13. package/dist/features/Comments/CommentService.mock.d.ts +4 -0
  14. package/dist/features/Comments/CommentService.mock.js +67 -0
  15. package/dist/features/Comments/CommentThread.d.ts +11 -0
  16. package/dist/features/Comments/CommentThread.js +14 -0
  17. package/dist/features/Comments/CommentUtils.d.ts +24 -0
  18. package/dist/features/Comments/CommentUtils.js +175 -0
  19. package/dist/features/Comments/Comments.d.ts +3 -1
  20. package/dist/features/Comments/Comments.js +254 -49
  21. package/dist/features/Comments/style.scss +178 -66
  22. package/dist/features/Comments/types/Comment.d.ts +0 -2
  23. package/dist/functions/StorybookUtils.d.ts +4 -0
  24. package/dist/functions/StorybookUtils.js +8 -0
  25. package/dist/functions/index.d.ts +1 -0
  26. package/dist/functions/index.js +1 -0
  27. package/package.json +4 -3
  28. package/dist/features/Comments/CommentAdd.js +0 -70
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import UsabilityUtils from '../../functions/UsabilityUtils';
3
+ import CommentInfo from './CommentInfo';
4
+ export default function CommentThread({ comments, hasEditAccess, onRemove, onUpdate, onEdit, onReply, }) {
5
+ const hasReplies = (thisComment) => comments.some((c) => c.reply === thisComment.id);
6
+ const getReplies = (thisComment) => comments.filter((c) => c.reply === thisComment.id);
7
+ return (_jsx(_Fragment, { children: comments.map((el, idx) => (_jsxs("div", { className: "thread", children: [!el.reply && (_jsx(CommentInfo, { comment: el, hasEditAccess: hasEditAccess, onRemove: () => onRemove(el.id), onUpdate: onUpdate, onEdit: () => {
8
+ onEdit(el);
9
+ UsabilityUtils.goToAnchor('comment-top');
10
+ }, onReply: () => {
11
+ onReply(el);
12
+ UsabilityUtils.goToAnchor('comment-top');
13
+ } })), hasReplies(el) && (_jsx("div", { className: "replies", children: getReplies(el).map((rel, idy) => (_jsx(CommentInfo, { comment: rel, hasEditAccess: hasEditAccess, onRemove: () => onRemove(rel.id), onUpdate: onUpdate, onEdit: () => onEdit(rel), onReply: () => onReply(rel) }, `${idx}_${idy}`))) }))] }, idx.valueOf()))) }));
14
+ }
@@ -0,0 +1,24 @@
1
+ export type DockPosition = 'left' | 'right' | 'bottom' | 'undocked';
2
+ export type DockedPosition = Exclude<DockPosition, 'undocked'>;
3
+ declare const CommentUtils: {
4
+ isDockPosition: (value: string | null) => value is DockPosition;
5
+ isDockedPosition: (value: DockPosition) => value is DockedPosition;
6
+ clampDrawerWidth: (width: number) => number;
7
+ clampDrawerHeight: (height: number) => number;
8
+ readStoredDrawerWidth: () => number;
9
+ readStoredDrawerHeight: () => number;
10
+ persistDrawerWidth: (width: number) => void;
11
+ persistDrawerHeight: (height: number) => void;
12
+ readStoredDockPosition: () => DockPosition;
13
+ persistDockPosition: (position: DockedPosition) => void;
14
+ getUndockedPopupSize: () => {
15
+ width: number;
16
+ height: number;
17
+ };
18
+ copyStylesToPopup: (popup: Window) => void;
19
+ openCommentsPopup: () => {
20
+ popup: Window;
21
+ container: HTMLElement;
22
+ } | null;
23
+ };
24
+ export default CommentUtils;
@@ -0,0 +1,175 @@
1
+ const DRAWER_WIDTH_STORAGE_KEY = 'tycho-comments-drawer-width';
2
+ const DRAWER_HEIGHT_STORAGE_KEY = 'tycho-comments-drawer-height';
3
+ const DOCK_POSITION_STORAGE_KEY = 'tycho-comments-dock-position';
4
+ const DRAWER_MIN_WIDTH = 320;
5
+ const DRAWER_MIN_HEIGHT = 200;
6
+ const POPUP_NAME = 'tycho-comments';
7
+ const POPUP_MIN_WIDTH = 960;
8
+ const POPUP_MIN_HEIGHT = 720;
9
+ const isDockPosition = (value) => value === 'left' ||
10
+ value === 'right' ||
11
+ value === 'bottom' ||
12
+ value === 'undocked';
13
+ const isDockedPosition = (value) => value !== 'undocked';
14
+ const getDefaultDrawerWidth = () => {
15
+ if (typeof window === 'undefined')
16
+ return 480;
17
+ const { innerWidth } = window;
18
+ if (innerWidth <= 767)
19
+ return Math.round(innerWidth * 0.9);
20
+ if (innerWidth <= 768)
21
+ return Math.round(innerWidth * 0.6);
22
+ return Math.round(innerWidth * 0.4);
23
+ };
24
+ const getDefaultDrawerHeight = () => {
25
+ if (typeof window === 'undefined')
26
+ return 360;
27
+ return Math.round(window.innerHeight * 0.4);
28
+ };
29
+ const clampDrawerWidth = (width) => {
30
+ if (typeof window === 'undefined')
31
+ return width;
32
+ const maxWidth = Math.min(Math.round(window.innerWidth * 0.9), window.innerWidth - 48);
33
+ return Math.min(Math.max(width, DRAWER_MIN_WIDTH), maxWidth);
34
+ };
35
+ const clampDrawerHeight = (height) => {
36
+ if (typeof window === 'undefined')
37
+ return height;
38
+ const maxHeight = Math.min(Math.round(window.innerHeight * 0.9), window.innerHeight - 48);
39
+ return Math.min(Math.max(height, DRAWER_MIN_HEIGHT), maxHeight);
40
+ };
41
+ const readStoredDrawerWidth = () => {
42
+ try {
43
+ const stored = localStorage.getItem(DRAWER_WIDTH_STORAGE_KEY);
44
+ if (stored) {
45
+ const parsed = Number(stored);
46
+ if (!Number.isNaN(parsed) && parsed > 0) {
47
+ return clampDrawerWidth(parsed);
48
+ }
49
+ }
50
+ }
51
+ catch {
52
+ // ignore storage access errors
53
+ }
54
+ return clampDrawerWidth(getDefaultDrawerWidth());
55
+ };
56
+ const readStoredDrawerHeight = () => {
57
+ try {
58
+ const stored = localStorage.getItem(DRAWER_HEIGHT_STORAGE_KEY);
59
+ if (stored) {
60
+ const parsed = Number(stored);
61
+ if (!Number.isNaN(parsed) && parsed > 0) {
62
+ return clampDrawerHeight(parsed);
63
+ }
64
+ }
65
+ }
66
+ catch {
67
+ // ignore storage access errors
68
+ }
69
+ return clampDrawerHeight(getDefaultDrawerHeight());
70
+ };
71
+ const persistDrawerWidth = (width) => {
72
+ try {
73
+ localStorage.setItem(DRAWER_WIDTH_STORAGE_KEY, String(width));
74
+ }
75
+ catch {
76
+ // ignore storage access errors
77
+ }
78
+ };
79
+ const persistDrawerHeight = (height) => {
80
+ try {
81
+ localStorage.setItem(DRAWER_HEIGHT_STORAGE_KEY, String(height));
82
+ }
83
+ catch {
84
+ // ignore storage access errors
85
+ }
86
+ };
87
+ const readStoredDockPosition = () => {
88
+ try {
89
+ const stored = localStorage.getItem(DOCK_POSITION_STORAGE_KEY);
90
+ if (isDockPosition(stored) && stored !== 'undocked') {
91
+ return stored;
92
+ }
93
+ }
94
+ catch {
95
+ // ignore storage access errors
96
+ }
97
+ return 'right';
98
+ };
99
+ const persistDockPosition = (position) => {
100
+ try {
101
+ localStorage.setItem(DOCK_POSITION_STORAGE_KEY, position);
102
+ }
103
+ catch {
104
+ // ignore storage access errors
105
+ }
106
+ };
107
+ const getUndockedPopupSize = () => {
108
+ if (typeof window === 'undefined') {
109
+ return { width: POPUP_MIN_WIDTH, height: POPUP_MIN_HEIGHT };
110
+ }
111
+ const availWidth = window.screen.availWidth || window.outerWidth;
112
+ const availHeight = window.screen.availHeight || window.outerHeight;
113
+ const width = Math.min(Math.max(Math.round(availWidth * 0.75), POPUP_MIN_WIDTH), availWidth - 40);
114
+ const height = Math.min(Math.max(Math.round(availHeight * 0.85), POPUP_MIN_HEIGHT), availHeight - 40);
115
+ return { width, height };
116
+ };
117
+ const copyStylesToPopup = (popup) => {
118
+ const { document: popupDoc } = popup;
119
+ popupDoc.head.innerHTML = '';
120
+ popupDoc.title = document.title;
121
+ document.querySelectorAll('link[rel="stylesheet"], style').forEach((node) => {
122
+ popupDoc.head.appendChild(node.cloneNode(true));
123
+ });
124
+ popupDoc.body.className = document.body.className;
125
+ popupDoc.body.style.margin = '0';
126
+ popupDoc.body.style.height = '100%';
127
+ popupDoc.documentElement.style.height = '100%';
128
+ popupDoc.documentElement.style.overflow = 'hidden';
129
+ };
130
+ const openCommentsPopup = () => {
131
+ if (typeof window === 'undefined')
132
+ return null;
133
+ const { width, height } = getUndockedPopupSize();
134
+ const left = Math.round(window.screenX + (window.outerWidth - width) / 2);
135
+ const top = Math.round(window.screenY + (window.outerHeight - height) / 2);
136
+ const features = [
137
+ 'popup=yes',
138
+ `width=${width}`,
139
+ `height=${height}`,
140
+ `left=${left}`,
141
+ `top=${top}`,
142
+ ].join(',');
143
+ const popup = window.open('', POPUP_NAME, features);
144
+ if (!popup)
145
+ return null;
146
+ try {
147
+ popup.resizeTo(width, height);
148
+ popup.moveTo(left, top);
149
+ }
150
+ catch {
151
+ // some browsers block resize/move on already-open named windows
152
+ }
153
+ copyStylesToPopup(popup);
154
+ const container = popup.document.createElement('div');
155
+ container.id = 'tycho-comments-root';
156
+ container.style.height = '100%';
157
+ popup.document.body.appendChild(container);
158
+ return { popup, container };
159
+ };
160
+ const CommentUtils = {
161
+ isDockPosition,
162
+ isDockedPosition,
163
+ clampDrawerWidth,
164
+ clampDrawerHeight,
165
+ readStoredDrawerWidth,
166
+ readStoredDrawerHeight,
167
+ persistDrawerWidth,
168
+ persistDrawerHeight,
169
+ readStoredDockPosition,
170
+ persistDockPosition,
171
+ getUndockedPopupSize,
172
+ copyStylesToPopup,
173
+ openCommentsPopup,
174
+ };
175
+ export default CommentUtils;
@@ -7,6 +7,8 @@ type Props = {
7
7
  onClose: () => void;
8
8
  mode: 'lexicon' | 'corpus' | 'parser';
9
9
  onChange?: (a: Comment[]) => void;
10
+ /** Opens the add-comment panel on mount (useful in Storybook). */
11
+ initialOpenAdd?: boolean;
10
12
  };
11
- export default function Comments({ uid, keywords, references, mode, onClose, onChange, }: Props): import("react").JSX.Element;
13
+ export default function Comments({ uid, keywords, references, mode, onClose, onChange, initialOpenAdd, }: Props): import("react").JSX.Element;
12
14
  export {};
@@ -1,23 +1,53 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Drawer } from '@mui/material';
3
- import { useEffect, useMemo, useState } from 'react';
3
+ import cx from 'classnames';
4
+ import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
5
+ import { createPortal } from 'react-dom';
4
6
  import { useTranslation } from 'react-i18next';
5
- import { Button, IconButton } from 'tycho-storybook';
7
+ import { Button, DropdownMenu, IconButton } from 'tycho-storybook';
6
8
  import AppLoading from '../../common/AppLoading';
7
9
  import AppPlaceholder from '../../common/AppPlaceholder';
8
10
  import SecurityUtils from '../../functions/SecurityUtils';
9
- import UsabilityUtils from '../../functions/UsabilityUtils';
10
11
  import CommentAdd from './CommentAdd';
11
- import CommentInfo from './CommentInfo';
12
12
  import CommentService from './CommentService';
13
+ import CommentThread from './CommentThread';
14
+ import CommentUtils from './CommentUtils';
13
15
  import './style.scss';
14
- export default function Comments({ uid, keywords, references, mode, onClose, onChange, }) {
16
+ function CommentsPanel({ dockPosition, showResizeHandle, onResizePointerDown, children, }) {
17
+ const isHorizontal = dockPosition === 'bottom';
18
+ return (_jsxs("div", { className: cx('comments-container', {
19
+ 'dock-left': dockPosition === 'left',
20
+ 'dock-right': dockPosition === 'right',
21
+ 'dock-bottom': dockPosition === 'bottom',
22
+ 'dock-undocked': dockPosition === 'undocked',
23
+ }), id: "comment-top", children: [showResizeHandle && (_jsx("div", { className: "comments-resize-handle", onPointerDown: onResizePointerDown, role: "separator", "aria-orientation": isHorizontal ? 'horizontal' : 'vertical', "aria-label": "Resize comments panel" })), children] }));
24
+ }
25
+ function CommentsHeader({ hasEditAccess, onToggleAdd, onClose, dockMenuOptions, }) {
26
+ const { t } = useTranslation('comments');
27
+ return (_jsxs("div", { className: "header", children: [_jsx(IconButton, { name: "close", size: "small", mode: "ghost", onClick: onClose }), _jsx("span", { className: "title", children: t('label.title.comments') }), _jsxs("div", { className: "actions", children: [hasEditAccess && (_jsx(Button, { text: t('button.add'), icon: "add", mode: "outlined", size: "small", className: "edit-button", onClick: onToggleAdd })), _jsx(DropdownMenu, { list: dockMenuOptions })] })] }));
28
+ }
29
+ export default function Comments({ uid, keywords, references, mode, onClose, onChange, initialOpenAdd = false, }) {
15
30
  const { t } = useTranslation('comments');
16
- const [openAddComment, setOpenAddComment] = useState(false);
31
+ const [openAddComment, setOpenAddComment] = useState(initialOpenAdd);
17
32
  const [comment, setComment] = useState();
18
33
  const [comments, setComments] = useState();
19
34
  const [users, setUsers] = useState([]);
20
35
  const [reply, setReply] = useState();
36
+ const [drawerWidth, setDrawerWidth] = useState(CommentUtils.readStoredDrawerWidth);
37
+ const [drawerHeight, setDrawerHeight] = useState(CommentUtils.readStoredDrawerHeight);
38
+ const [dockPosition, setDockPosition] = useState(CommentUtils.readStoredDockPosition);
39
+ const [externalContainer, setExternalContainer] = useState(null);
40
+ const commentAddRef = useRef(null);
41
+ const popupRef = useRef(null);
42
+ const popupPollRef = useRef(null);
43
+ const ignorePopupCloseRef = useRef(false);
44
+ const lastDockedRef = useRef(null);
45
+ if (lastDockedRef.current === null) {
46
+ const initial = CommentUtils.readStoredDockPosition();
47
+ lastDockedRef.current = CommentUtils.isDockedPosition(initial)
48
+ ? initial
49
+ : 'right';
50
+ }
21
51
  const load = async () => {
22
52
  try {
23
53
  const [commentsResponse, usersResponse] = await Promise.all([
@@ -29,21 +59,216 @@ export default function Comments({ uid, keywords, references, mode, onClose, onC
29
59
  }
30
60
  catch (error) {
31
61
  console.error('Error loading comments or users', error);
32
- // optionally handle specific error state here
33
62
  }
34
63
  };
35
64
  const hasEditAccess = useMemo(() => {
36
65
  return SecurityUtils.hasAccess(uid, ['ADMIN', 'EDITOR'], mode);
37
66
  }, [uid, mode]);
38
- const hasReplies = (thisComment) => comments && comments.some((c) => c.reply === thisComment.id);
39
- const getReplies = (thisComment) => comments?.filter((c) => c.reply === thisComment.id) || [];
67
+ const clearPopupPoll = useCallback(() => {
68
+ if (popupPollRef.current != null) {
69
+ window.clearInterval(popupPollRef.current);
70
+ popupPollRef.current = null;
71
+ }
72
+ }, []);
73
+ const redockFromPopup = useCallback(() => {
74
+ clearPopupPoll();
75
+ popupRef.current = null;
76
+ setExternalContainer(null);
77
+ if (ignorePopupCloseRef.current) {
78
+ ignorePopupCloseRef.current = false;
79
+ return;
80
+ }
81
+ setDockPosition((prev) => {
82
+ if (prev !== 'undocked')
83
+ return prev;
84
+ const next = lastDockedRef.current ?? 'right';
85
+ CommentUtils.persistDockPosition(next);
86
+ return next;
87
+ });
88
+ }, [clearPopupPoll]);
89
+ const closePopup = useCallback(() => {
90
+ clearPopupPoll();
91
+ const popup = popupRef.current;
92
+ if (popup && !popup.closed) {
93
+ ignorePopupCloseRef.current = true;
94
+ popup.close();
95
+ }
96
+ popupRef.current = null;
97
+ setExternalContainer(null);
98
+ }, [clearPopupPoll]);
99
+ const handleClose = useCallback(() => {
100
+ closePopup();
101
+ onClose();
102
+ }, [closePopup, onClose]);
103
+ const openUndockedWindow = useCallback(() => {
104
+ const existing = popupRef.current;
105
+ if (existing && !existing.closed) {
106
+ existing.focus();
107
+ setDockPosition('undocked');
108
+ return;
109
+ }
110
+ const opened = CommentUtils.openCommentsPopup();
111
+ if (!opened)
112
+ return;
113
+ const { popup, container } = opened;
114
+ ignorePopupCloseRef.current = false;
115
+ popupRef.current = popup;
116
+ setExternalContainer(container);
117
+ setDockPosition('undocked');
118
+ popup.addEventListener('beforeunload', () => {
119
+ redockFromPopup();
120
+ });
121
+ clearPopupPoll();
122
+ popupPollRef.current = window.setInterval(() => {
123
+ if (!popupRef.current || popupRef.current.closed) {
124
+ redockFromPopup();
125
+ }
126
+ }, 500);
127
+ }, [clearPopupPoll, redockFromPopup]);
128
+ const setDockedPosition = useCallback((position) => {
129
+ lastDockedRef.current = position;
130
+ CommentUtils.persistDockPosition(position);
131
+ closePopup();
132
+ setDockPosition(position);
133
+ }, [closePopup]);
134
+ const dockMenuOptions = useMemo(() => [
135
+ {
136
+ label: t('menu.undock'),
137
+ icon: 'open_in_new',
138
+ disabled: dockPosition === 'undocked',
139
+ onClick: () => {
140
+ if (dockPosition === 'undocked')
141
+ return;
142
+ openUndockedWindow();
143
+ },
144
+ },
145
+ {
146
+ label: t('menu.dock.left'),
147
+ icon: 'dock_to_left',
148
+ disabled: dockPosition === 'left',
149
+ onClick: () => {
150
+ if (dockPosition === 'left')
151
+ return;
152
+ setDockedPosition('left');
153
+ },
154
+ },
155
+ {
156
+ label: t('menu.dock.bottom'),
157
+ icon: 'dock_to_bottom',
158
+ disabled: dockPosition === 'bottom',
159
+ onClick: () => {
160
+ if (dockPosition === 'bottom')
161
+ return;
162
+ setDockedPosition('bottom');
163
+ },
164
+ },
165
+ {
166
+ label: t('menu.dock.right'),
167
+ icon: 'dock_to_right',
168
+ disabled: dockPosition === 'right',
169
+ onClick: () => {
170
+ if (dockPosition === 'right')
171
+ return;
172
+ setDockedPosition('right');
173
+ },
174
+ },
175
+ ], [dockPosition, openUndockedWindow, setDockedPosition, t]);
40
176
  useEffect(() => {
41
177
  load();
42
178
  }, []);
43
- return (_jsx(Drawer, { anchor: "right", open: true, onClose: onClose, children: !comments ? (_jsx(AppLoading, {})) : (_jsxs("div", { className: "comments-container", id: "comment-top", children: [_jsxs("div", { className: "header", children: [_jsx(IconButton, { name: "close", size: "small", mode: "ghost", onClick: onClose }), _jsx("span", { className: "title", children: t('label.title.comments') }), _jsx("div", { className: "actions", children: hasEditAccess && (_jsx(Button, { text: t('button.add'), icon: "add", mode: "outlined", size: "small", className: "edit-button", onClick: () => {
44
- setComment(undefined);
45
- setOpenAddComment(!openAddComment);
46
- } })) })] }), _jsxs("div", { className: "body", children: [hasEditAccess && openAddComment && (_jsx(CommentAdd, { uid: uid, mode: mode, users: users, references: references, keywords: keywords, comment: comment, reply: reply, onClose: () => {
179
+ useEffect(() => {
180
+ if (!openAddComment || !comments)
181
+ return;
182
+ const frame = requestAnimationFrame(() => {
183
+ commentAddRef.current?.scrollIntoView({
184
+ behavior: 'smooth',
185
+ block: 'nearest',
186
+ });
187
+ });
188
+ return () => cancelAnimationFrame(frame);
189
+ }, [openAddComment, comments, comment, reply]);
190
+ useEffect(() => {
191
+ const onHostUnload = () => {
192
+ closePopup();
193
+ };
194
+ window.addEventListener('pagehide', onHostUnload);
195
+ window.addEventListener('beforeunload', onHostUnload);
196
+ return () => {
197
+ window.removeEventListener('pagehide', onHostUnload);
198
+ window.removeEventListener('beforeunload', onHostUnload);
199
+ closePopup();
200
+ };
201
+ }, [closePopup]);
202
+ const handleResizePointerDown = useCallback((event) => {
203
+ event.preventDefault();
204
+ const handle = event.currentTarget;
205
+ handle.setPointerCapture(event.pointerId);
206
+ const previousUserSelect = document.body.style.userSelect;
207
+ const previousCursor = document.body.style.cursor;
208
+ const isBottom = dockPosition === 'bottom';
209
+ const isLeft = dockPosition === 'left';
210
+ document.body.style.userSelect = 'none';
211
+ document.body.style.cursor = isBottom ? 'row-resize' : 'col-resize';
212
+ const computeSize = (clientX, clientY) => {
213
+ if (isBottom) {
214
+ return CommentUtils.clampDrawerHeight(window.innerHeight - clientY);
215
+ }
216
+ if (isLeft) {
217
+ return CommentUtils.clampDrawerWidth(clientX);
218
+ }
219
+ return CommentUtils.clampDrawerWidth(window.innerWidth - clientX);
220
+ };
221
+ const onPointerMove = (moveEvent) => {
222
+ const next = computeSize(moveEvent.clientX, moveEvent.clientY);
223
+ if (isBottom) {
224
+ setDrawerHeight(next);
225
+ }
226
+ else {
227
+ setDrawerWidth(next);
228
+ }
229
+ };
230
+ const onPointerUp = (upEvent) => {
231
+ const next = computeSize(upEvent.clientX, upEvent.clientY);
232
+ if (isBottom) {
233
+ setDrawerHeight(next);
234
+ CommentUtils.persistDrawerHeight(next);
235
+ }
236
+ else {
237
+ setDrawerWidth(next);
238
+ CommentUtils.persistDrawerWidth(next);
239
+ }
240
+ document.body.style.userSelect = previousUserSelect;
241
+ document.body.style.cursor = previousCursor;
242
+ handle.releasePointerCapture(upEvent.pointerId);
243
+ handle.removeEventListener('pointermove', onPointerMove);
244
+ handle.removeEventListener('pointerup', onPointerUp);
245
+ handle.removeEventListener('pointercancel', onPointerUp);
246
+ };
247
+ handle.addEventListener('pointermove', onPointerMove);
248
+ handle.addEventListener('pointerup', onPointerUp);
249
+ handle.addEventListener('pointercancel', onPointerUp);
250
+ }, [dockPosition]);
251
+ const paperStyle = dockPosition === 'bottom'
252
+ ? { height: drawerHeight, width: '100%' }
253
+ : { width: drawerWidth };
254
+ const content = !comments ? (_jsx(AppLoading, { pageLoading: true })) : (_jsxs(_Fragment, { children: [_jsx(CommentsHeader, { hasEditAccess: hasEditAccess, onToggleAdd: () => {
255
+ setComment(undefined);
256
+ setOpenAddComment(!openAddComment);
257
+ }, onClose: handleClose, dockMenuOptions: dockMenuOptions }), _jsxs("div", { className: "body", children: [comments.length === 0 && !openAddComment && (_jsx(AppPlaceholder, { text: t('placeholder.comments.empty'), useMarginTop: true })), _jsx(CommentThread, { comments: comments, hasEditAccess: hasEditAccess, onRemove: (id) => {
258
+ const updated = comments.filter((c) => c.id !== id);
259
+ setComments(updated);
260
+ onChange?.(updated);
261
+ }, onUpdate: (thisComment) => {
262
+ const updated = comments.map((c) => c.id === thisComment.id ? { ...c, ...thisComment } : c);
263
+ setComments(updated);
264
+ onChange?.(updated);
265
+ }, onEdit: (el) => {
266
+ setComment(el);
267
+ setOpenAddComment(true);
268
+ }, onReply: (el) => {
269
+ setReply(el);
270
+ setOpenAddComment(true);
271
+ } }), hasEditAccess && openAddComment && (_jsx("div", { ref: commentAddRef, children: _jsx(CommentAdd, { uid: uid, mode: mode, users: users, references: references, keywords: keywords, comment: comment, reply: reply, onClose: () => {
47
272
  setComment(undefined);
48
273
  setReply(undefined);
49
274
  setOpenAddComment(false);
@@ -54,42 +279,22 @@ export default function Comments({ uid, keywords, references, mode, onClose, onC
54
279
  onChange?.(updated);
55
280
  }
56
281
  else {
57
- const updated = comments?.map((c) => (c.id === comment.id ? el : c)) ||
58
- [];
282
+ const updated = comments?.map((c) => (c.id === comment.id ? el : c)) || [];
59
283
  setComments(updated);
60
284
  onChange?.(updated);
61
285
  }
62
- } })), comments?.length === 0 && (_jsx(AppPlaceholder, { text: t('placeholder.comments.empty') })), comments?.map((el, idx) => (_jsxs("div", { className: "thread", children: [!el.reply && (_jsx(CommentInfo, { comment: el, hasEditAccess: hasEditAccess, onRemove: () => {
63
- const updated = comments?.filter((c) => c.id !== el.id);
64
- setComments(updated);
65
- onChange && onChange(updated || []);
66
- }, onUpdate: (thisComment) => {
67
- const updated = comments?.map((c) => c.id === thisComment.id ? { ...c, ...thisComment } : c) || [];
68
- setComments(updated);
69
- onChange?.(updated);
70
- }, onEdit: () => {
71
- setComment(el);
72
- setOpenAddComment(true);
73
- UsabilityUtils.goToAnchor('comment-top');
74
- }, onReply: () => {
75
- setReply(el);
76
- setOpenAddComment(true);
77
- UsabilityUtils.goToAnchor('comment-top');
78
- } })), hasReplies(el) && (_jsx("div", { className: "replies", children: getReplies(el).map((rel, idy) => (_jsx(CommentInfo, { comment: rel, hasEditAccess: hasEditAccess, onRemove: () => {
79
- const updated = comments?.filter((c) => c.id !== rel.id);
80
- setComments(updated);
81
- onChange && onChange(updated || []);
82
- }, onUpdate: (thisComment) => {
83
- const updated = comments?.map((c) => c.id === thisComment.id
84
- ? { ...c, ...thisComment }
85
- : c) || [];
86
- setComments(updated);
87
- onChange?.(updated);
88
- }, onEdit: () => {
89
- setComment(rel);
90
- setOpenAddComment(true);
91
- }, onReply: () => {
92
- setReply(rel);
93
- setOpenAddComment(true);
94
- } }, `${idx}_${idy}`))) }))] }, idx.valueOf())))] })] })) }));
286
+ } }) }))] })] }));
287
+ const panel = (_jsx(CommentsPanel, { dockPosition: dockPosition, showResizeHandle: CommentUtils.isDockedPosition(dockPosition), onResizePointerDown: handleResizePointerDown, children: content }));
288
+ const isDocked = CommentUtils.isDockedPosition(dockPosition);
289
+ const drawerAnchor = isDocked
290
+ ? dockPosition
291
+ : (lastDockedRef.current ?? 'right');
292
+ return (_jsxs(_Fragment, { children: [_jsx(Drawer, { anchor: drawerAnchor, open: isDocked, onClose: handleClose, slotProps: {
293
+ backdrop: { invisible: true },
294
+ }, PaperProps: {
295
+ className: cx('comments-drawer-paper', `dock-${drawerAnchor}`),
296
+ style: paperStyle,
297
+ }, children: isDocked ? panel : null }), dockPosition === 'undocked' &&
298
+ externalContainer &&
299
+ createPortal(_jsx("div", { className: "comments-undocked-root", children: panel }), externalContainer)] }));
95
300
  }