tycho-components 0.42.0 → 0.42.2
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/dist/configs/localization/CommentsTexts.d.ts +30 -0
- package/dist/configs/localization/CommentsTexts.js +30 -0
- package/dist/features/Comments/CommentDetails/CommentDetails.d.ts +8 -0
- package/dist/features/Comments/CommentDetails/CommentDetails.js +15 -0
- package/dist/features/Comments/CommentDetails/index.d.ts +2 -0
- package/dist/features/Comments/CommentDetails/index.js +2 -0
- package/dist/features/Comments/CommentDetails/styles.scss +87 -0
- package/dist/features/Comments/{CommentInfo.d.ts → CommentInfo/CommentInfo.d.ts} +2 -2
- package/dist/features/Comments/CommentInfo/CommentInfo.js +74 -0
- package/dist/features/Comments/CommentInfo/index.d.ts +2 -0
- package/dist/features/Comments/CommentInfo/index.js +2 -0
- package/dist/features/Comments/CommentInfo/styles.scss +95 -0
- package/dist/features/Comments/CommentService.d.ts +2 -0
- package/dist/features/Comments/CommentService.js +8 -0
- package/dist/features/Comments/CommentService.mock.js +1 -0
- package/dist/features/Comments/{CommentThread.d.ts → CommentThread/CommentThread.d.ts} +2 -1
- package/dist/features/Comments/{CommentThread.js → CommentThread/CommentThread.js} +3 -2
- package/dist/features/Comments/CommentThread/index.d.ts +2 -0
- package/dist/features/Comments/CommentThread/index.js +2 -0
- package/dist/features/Comments/CommentThread/styles.scss +16 -0
- package/dist/features/Comments/Comments.js +2 -51
- package/dist/features/Comments/style.scss +0 -110
- package/dist/features/Comments/types/Comment.d.ts +2 -0
- package/dist/features/Comments/useCommentsResize.d.ts +7 -0
- package/dist/features/Comments/useCommentsResize.js +60 -0
- package/dist/features/PageRender/collapseElementsForTier.js +115 -3
- package/package.json +3 -3
- package/dist/features/Comments/CommentInfo.js +0 -35
|
@@ -15,6 +15,15 @@ export declare const CommentsTexts: {
|
|
|
15
15
|
"tooltip.delete": string;
|
|
16
16
|
"tooltip.read": string;
|
|
17
17
|
"tooltip.reply": string;
|
|
18
|
+
"tooltip.resolve": string;
|
|
19
|
+
"tooltip.details": string;
|
|
20
|
+
"modal.title.details": string;
|
|
21
|
+
"label.author": string;
|
|
22
|
+
"label.edited.on": string;
|
|
23
|
+
"label.read": string;
|
|
24
|
+
"label.unread": string;
|
|
25
|
+
"label.read.on": string;
|
|
26
|
+
"label.requested": string;
|
|
18
27
|
"modal.title.footnote": string;
|
|
19
28
|
"placeholder.footnote.text": string;
|
|
20
29
|
"modal.title.footnote.remove": string;
|
|
@@ -31,6 +40,7 @@ export declare const CommentsTexts: {
|
|
|
31
40
|
"notification.empty": string;
|
|
32
41
|
"notification.text": string;
|
|
33
42
|
"label.edited": string;
|
|
43
|
+
"label.resolved": string;
|
|
34
44
|
"tab.write": string;
|
|
35
45
|
"tab.preview": string;
|
|
36
46
|
"placeholder.preview.empty": string;
|
|
@@ -55,6 +65,15 @@ export declare const CommentsTexts: {
|
|
|
55
65
|
"tooltip.delete": string;
|
|
56
66
|
"tooltip.read": string;
|
|
57
67
|
"tooltip.reply": string;
|
|
68
|
+
"tooltip.resolve": string;
|
|
69
|
+
"tooltip.details": string;
|
|
70
|
+
"modal.title.details": string;
|
|
71
|
+
"label.author": string;
|
|
72
|
+
"label.edited.on": string;
|
|
73
|
+
"label.read": string;
|
|
74
|
+
"label.unread": string;
|
|
75
|
+
"label.read.on": string;
|
|
76
|
+
"label.requested": string;
|
|
58
77
|
"modal.title.footnote": string;
|
|
59
78
|
"placeholder.footnote.text": string;
|
|
60
79
|
"modal.title.footnote.remove": string;
|
|
@@ -71,6 +90,7 @@ export declare const CommentsTexts: {
|
|
|
71
90
|
"notification.empty": string;
|
|
72
91
|
"notification.text": string;
|
|
73
92
|
"label.edited": string;
|
|
93
|
+
"label.resolved": string;
|
|
74
94
|
"tab.write": string;
|
|
75
95
|
"tab.preview": string;
|
|
76
96
|
"placeholder.preview.empty": string;
|
|
@@ -95,6 +115,15 @@ export declare const CommentsTexts: {
|
|
|
95
115
|
"tooltip.delete": string;
|
|
96
116
|
"tooltip.read": string;
|
|
97
117
|
"tooltip.reply": string;
|
|
118
|
+
"tooltip.resolve": string;
|
|
119
|
+
"tooltip.details": string;
|
|
120
|
+
"modal.title.details": string;
|
|
121
|
+
"label.author": string;
|
|
122
|
+
"label.edited.on": string;
|
|
123
|
+
"label.read": string;
|
|
124
|
+
"label.unread": string;
|
|
125
|
+
"label.read.on": string;
|
|
126
|
+
"label.requested": string;
|
|
98
127
|
"modal.title.footnote": string;
|
|
99
128
|
"placeholder.footnote.text": string;
|
|
100
129
|
"modal.title.footnote.remove": string;
|
|
@@ -111,6 +140,7 @@ export declare const CommentsTexts: {
|
|
|
111
140
|
"notification.empty": string;
|
|
112
141
|
"notification.text": string;
|
|
113
142
|
"label.edited": string;
|
|
143
|
+
"label.resolved": string;
|
|
114
144
|
"tab.write": string;
|
|
115
145
|
"tab.preview": string;
|
|
116
146
|
"placeholder.preview.empty": string;
|
|
@@ -15,6 +15,15 @@ export const CommentsTexts = {
|
|
|
15
15
|
"tooltip.delete": "Remove comment",
|
|
16
16
|
"tooltip.read": "Mark as read",
|
|
17
17
|
"tooltip.reply": "Reply",
|
|
18
|
+
"tooltip.resolve": "Mark as resolved",
|
|
19
|
+
"tooltip.details": "See detailed information",
|
|
20
|
+
"modal.title.details": "Comment details",
|
|
21
|
+
"label.author": "Author",
|
|
22
|
+
"label.edited.on": "Edited on:",
|
|
23
|
+
"label.read": "Read",
|
|
24
|
+
"label.unread": "Unread",
|
|
25
|
+
"label.read.on": "Read on:",
|
|
26
|
+
"label.requested": "Requested",
|
|
18
27
|
"modal.title.footnote": "Edit footnote",
|
|
19
28
|
"placeholder.footnote.text": "Enter footnote",
|
|
20
29
|
"modal.title.footnote.remove": "Confirm footnote removal",
|
|
@@ -31,6 +40,7 @@ export const CommentsTexts = {
|
|
|
31
40
|
"notification.empty": "No more notifications",
|
|
32
41
|
"notification.text": "{{user}} added a new comment.",
|
|
33
42
|
"label.edited": "edited",
|
|
43
|
+
"label.resolved": "resolved",
|
|
34
44
|
"tab.write": "Write",
|
|
35
45
|
"tab.preview": "Preview",
|
|
36
46
|
"placeholder.preview.empty": "Nothing to preview",
|
|
@@ -55,6 +65,15 @@ export const CommentsTexts = {
|
|
|
55
65
|
"tooltip.delete": "Remover comentário",
|
|
56
66
|
"tooltip.read": "Marcar como lido",
|
|
57
67
|
"tooltip.reply": "Responder",
|
|
68
|
+
"tooltip.resolve": "Marcar como resolvido",
|
|
69
|
+
"tooltip.details": "Ver informações detalhadas",
|
|
70
|
+
"modal.title.details": "Detalhes do comentário",
|
|
71
|
+
"label.author": "Autor",
|
|
72
|
+
"label.edited.on": "Editado em:",
|
|
73
|
+
"label.read": "Lido",
|
|
74
|
+
"label.unread": "Não lido",
|
|
75
|
+
"label.read.on": "Lido em:",
|
|
76
|
+
"label.requested": "Solicitados",
|
|
58
77
|
"modal.title.footnote": "Editar nota de rodapé",
|
|
59
78
|
"placeholder.footnote.text": "Entre o texto da nota de rodapé",
|
|
60
79
|
"modal.title.footnote.remove": "Confirmação de exclusão de nota de rodapé",
|
|
@@ -71,6 +90,7 @@ export const CommentsTexts = {
|
|
|
71
90
|
"notification.empty": "Não há notificações",
|
|
72
91
|
"notification.text": "{{user}} adicionou um novo comentário.",
|
|
73
92
|
"label.edited": "editado",
|
|
93
|
+
"label.resolved": "resolvido",
|
|
74
94
|
"tab.write": "Escrever",
|
|
75
95
|
"tab.preview": "Visualizar",
|
|
76
96
|
"placeholder.preview.empty": "Nada para visualizar",
|
|
@@ -95,6 +115,15 @@ export const CommentsTexts = {
|
|
|
95
115
|
"tooltip.delete": "Rimuovi commento",
|
|
96
116
|
"tooltip.read": "Segnala come letto",
|
|
97
117
|
"tooltip.reply": "Rispondi",
|
|
118
|
+
"tooltip.resolve": "Segna come risolto",
|
|
119
|
+
"tooltip.details": "Vedi informazioni dettagliate",
|
|
120
|
+
"modal.title.details": "Dettagli del commento",
|
|
121
|
+
"label.author": "Autore",
|
|
122
|
+
"label.edited.on": "Modificato il:",
|
|
123
|
+
"label.read": "Letto",
|
|
124
|
+
"label.unread": "Non letto",
|
|
125
|
+
"label.read.on": "Letto il:",
|
|
126
|
+
"label.requested": "Richiesti",
|
|
98
127
|
"modal.title.footnote": "Modifica nota a piè di pagina",
|
|
99
128
|
"placeholder.footnote.text": "Inserisci nota a piè di pagina",
|
|
100
129
|
"modal.title.footnote.remove": "Conferma rimozione nota a piè di pagina",
|
|
@@ -111,6 +140,7 @@ export const CommentsTexts = {
|
|
|
111
140
|
"notification.empty": "Nessun'altra notifica",
|
|
112
141
|
"notification.text": "{{user}} ha aggiunto un commento all'inserimento {{entry}}.",
|
|
113
142
|
"label.edited": "modificato",
|
|
143
|
+
"label.resolved": "risolto",
|
|
114
144
|
"tab.write": "Scrivi",
|
|
115
145
|
"tab.preview": "Anteprima",
|
|
116
146
|
"placeholder.preview.empty": "Niente da visualizzare",
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { Avatar, Tag } from 'tycho-storybook';
|
|
4
|
+
import AppModal from '../../../common/AppModal/AppModal';
|
|
5
|
+
import DateUtils from '../../../functions/DateUtils';
|
|
6
|
+
import './styles.scss';
|
|
7
|
+
const DATE_TIME_FORMAT = 'MM/dd/yyyy - HH:mm';
|
|
8
|
+
export default function CommentDetails({ comment, onClose }) {
|
|
9
|
+
const { t } = useTranslation('comments');
|
|
10
|
+
const postedDateTime = DateUtils.formatDateTime(comment.date, DATE_TIME_FORMAT);
|
|
11
|
+
const editedDateTime = comment.edited
|
|
12
|
+
? DateUtils.formatDateTime(comment.edited, DATE_TIME_FORMAT)
|
|
13
|
+
: undefined;
|
|
14
|
+
return (_jsx(AppModal, { title: t('modal.title.details'), close: onClose, hideFooter: true, className: "comment-details-modal", children: _jsxs("div", { className: "comment-details", children: [_jsxs("div", { className: "comment-details-section", children: [_jsxs("div", { className: "comment-details-author", children: [_jsx(Avatar, { title: comment.name, src: comment.picture, size: "small" }), _jsxs("div", { className: "comment-details-author-meta", children: [_jsx("span", { className: "comment-details-label", children: t('label.author') }), _jsx("span", { className: "comment-details-value", children: comment.name })] }), comment.resolved && (_jsx(Tag, { text: t('label.resolved'), color: "green", size: "small" }))] }), _jsxs("div", { className: "comment-details-row", children: [_jsx("span", { className: "comment-details-label", children: t('label.posted.on') }), _jsx("span", { className: "comment-details-label", children: postedDateTime })] }), editedDateTime && (_jsxs("div", { className: "comment-details-row", children: [_jsx("span", { className: "comment-details-label", children: t('label.edited.on') }), _jsx("span", { className: "comment-details-label", children: editedDateTime })] }))] }), comment.requested && comment.requested.length > 0 && (_jsxs("div", { className: "comment-details-section", children: [_jsx("span", { className: "comment-details-section-title", children: t('label.requested') }), _jsx("ul", { className: "comment-details-requested", children: comment.requested.map((user) => (_jsxs("li", { className: "comment-details-requested-item", children: [_jsx(Avatar, { title: user.name, src: user.picture, size: "x-small" }), _jsxs("div", { className: "comment-details-requested-meta", children: [_jsx("span", { className: "comment-details-value", children: user.name }), user.email && (_jsx("span", { className: "comment-details-secondary", children: user.email })), user.read && user.dateRead && (_jsxs("span", { className: "comment-details-secondary", children: [t('label.read.on'), ' ', DateUtils.formatDateTime(user.dateRead, DATE_TIME_FORMAT)] }))] }), _jsx(Tag, { text: user.read ? t('label.read') : t('label.unread'), color: user.read ? 'green' : 'gray', size: "small" })] }, user.uid))) })] }))] }) }));
|
|
15
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
.comment-details-modal {
|
|
2
|
+
width: 36vw;
|
|
3
|
+
|
|
4
|
+
> .body {
|
|
5
|
+
padding: 32px 16px;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
.comment-details {
|
|
9
|
+
display: flex;
|
|
10
|
+
flex-direction: column;
|
|
11
|
+
gap: var(--spacing-300);
|
|
12
|
+
min-width: min(480px, 80vw);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.comment-details-section {
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
gap: var(--spacing-150);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.comment-details-section-title {
|
|
22
|
+
@include label-medium-1;
|
|
23
|
+
color: var(--text-secondary);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.comment-details-author {
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
gap: var(--spacing-150);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.comment-details-author-meta {
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-direction: column;
|
|
35
|
+
gap: var(--spacing-050);
|
|
36
|
+
min-width: 0;
|
|
37
|
+
flex: 1;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.comment-details-row {
|
|
41
|
+
display: flex;
|
|
42
|
+
flex-wrap: wrap;
|
|
43
|
+
gap: var(--spacing-050) var(--spacing-100);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.comment-details-label {
|
|
47
|
+
@include helper-small-1;
|
|
48
|
+
color: var(--text-tertiary);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.comment-details-value {
|
|
52
|
+
@include body-medium-1;
|
|
53
|
+
color: var(--text-primary);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.comment-details-secondary {
|
|
57
|
+
@include helper-small-1;
|
|
58
|
+
color: var(--text-tertiary);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.comment-details-author-meta .comment-details-value {
|
|
62
|
+
@include label-medium-1;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.comment-details-requested {
|
|
66
|
+
list-style: none;
|
|
67
|
+
margin: 0;
|
|
68
|
+
padding: 0;
|
|
69
|
+
display: flex;
|
|
70
|
+
flex-direction: column;
|
|
71
|
+
gap: var(--spacing-150);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.comment-details-requested-item {
|
|
75
|
+
display: flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
gap: var(--spacing-150);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.comment-details-requested-meta {
|
|
81
|
+
display: flex;
|
|
82
|
+
flex-direction: column;
|
|
83
|
+
gap: var(--spacing-050);
|
|
84
|
+
min-width: 0;
|
|
85
|
+
flex: 1;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import '@uiw/react-markdown-preview/markdown.css';
|
|
3
|
+
import MDEditor from '@uiw/react-md-editor';
|
|
4
|
+
import { useContext, useEffect, useRef, useState } from 'react';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
import { Avatar, DropdownMenu, Tag } from 'tycho-storybook';
|
|
7
|
+
import CommonContext from '../../../configs/CommonContext';
|
|
8
|
+
import DateUtils from '../../../functions/DateUtils';
|
|
9
|
+
import CommentDetails from '../CommentDetails';
|
|
10
|
+
import CommentService from '../CommentService';
|
|
11
|
+
import './styles.scss';
|
|
12
|
+
export default function CommentInfo({ comment, hasEditAccess = true, onRemove, onUpdate, onEdit, onReply, }) {
|
|
13
|
+
const { t } = useTranslation('comments');
|
|
14
|
+
const { state } = useContext(CommonContext);
|
|
15
|
+
const markedReadRef = useRef(false);
|
|
16
|
+
const [showDetails, setShowDetails] = useState(false);
|
|
17
|
+
const isNotRead = (thisComment) => {
|
|
18
|
+
if (!thisComment.requested)
|
|
19
|
+
return false;
|
|
20
|
+
const requested = thisComment.requested.find((r) => r.uid === state.logged?.uid);
|
|
21
|
+
if (!requested)
|
|
22
|
+
return false;
|
|
23
|
+
return !requested.read;
|
|
24
|
+
};
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (!isNotRead(comment) || markedReadRef.current)
|
|
27
|
+
return;
|
|
28
|
+
markedReadRef.current = true;
|
|
29
|
+
CommentService.markRead(comment.id).then((r) => onUpdate(r.data));
|
|
30
|
+
}, [comment, onUpdate]);
|
|
31
|
+
const handleRemove = () => {
|
|
32
|
+
CommentService.remove(comment.id).then(() => {
|
|
33
|
+
onRemove();
|
|
34
|
+
});
|
|
35
|
+
};
|
|
36
|
+
const handleResolve = () => {
|
|
37
|
+
CommentService.resolve(comment.id).then((r) => onUpdate(r.data));
|
|
38
|
+
};
|
|
39
|
+
const isAuthor = hasEditAccess && state.logged?.uid === comment.user;
|
|
40
|
+
const menuOptions = [];
|
|
41
|
+
menuOptions.push({
|
|
42
|
+
label: t('tooltip.details'),
|
|
43
|
+
icon: 'info',
|
|
44
|
+
onClick: () => setShowDetails(true),
|
|
45
|
+
});
|
|
46
|
+
if (hasEditAccess && !comment.reply) {
|
|
47
|
+
menuOptions.push({
|
|
48
|
+
label: t('tooltip.reply'),
|
|
49
|
+
icon: 'reply',
|
|
50
|
+
onClick: onReply,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (isAuthor) {
|
|
54
|
+
menuOptions.push({
|
|
55
|
+
label: t('tooltip.edit'),
|
|
56
|
+
icon: 'edit',
|
|
57
|
+
onClick: onEdit,
|
|
58
|
+
}, {
|
|
59
|
+
label: t('tooltip.delete'),
|
|
60
|
+
icon: 'delete',
|
|
61
|
+
onClick: handleRemove,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (hasEditAccess && !comment.reply && !comment.resolved) {
|
|
65
|
+
menuOptions.push({
|
|
66
|
+
label: t('tooltip.resolve'),
|
|
67
|
+
icon: 'check',
|
|
68
|
+
onClick: handleResolve,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
const displayDate = comment.edited || comment.date;
|
|
72
|
+
const fullDateTitle = DateUtils.formatDateTime(displayDate, 'MM/dd/yyyy - HH:mm');
|
|
73
|
+
return (_jsxs("div", { className: "comment", children: [_jsxs("div", { className: "comment-header", children: [_jsx(Avatar, { title: comment.name, src: comment.picture, size: "x-small" }), _jsxs("div", { className: "meta", children: [_jsx("span", { className: "name", children: comment.name }), _jsxs("span", { className: "timestamp", title: fullDateTitle, children: [t('label.on'), " ", DateUtils.formatDateTime(displayDate, 'MMM d')] })] }), _jsxs("div", { className: "buttons", children: [comment.resolved && (_jsx(Tag, { text: t('label.resolved'), color: "green", size: "small" })), comment.edited && !comment.resolved && (_jsx(Tag, { text: t('label.edited'), color: "blue", size: "small" })), menuOptions.length > 0 && (_jsx(DropdownMenu, { size: "small", className: "comment-menu", list: menuOptions }))] })] }), _jsxs("div", { className: "comment-body", children: [_jsx("div", { className: "text", "data-color-mode": "light", children: _jsx(MDEditor.Markdown, { source: comment.value }) }), comment.requested && (_jsxs("div", { className: "meta-line", children: [_jsx("span", { className: "me-1", children: t('label.requested.to') }), _jsx("span", { children: comment.requested.map((e) => e.name).join(', ') })] }))] }), showDetails && (_jsx(CommentDetails, { comment: comment, onClose: () => setShowDetails(false) }))] }));
|
|
74
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
.comment {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
border: 1px solid var(--border-subtle-1);
|
|
5
|
+
border-radius: var(--radius-200);
|
|
6
|
+
background-color: var(--background-default);
|
|
7
|
+
|
|
8
|
+
.comment-header {
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
gap: var(--spacing-100);
|
|
12
|
+
padding: 4px 8px;
|
|
13
|
+
background-color: var(--layer-hover-2);
|
|
14
|
+
border-bottom: 1px solid var(--border-subtle-1);
|
|
15
|
+
border-radius: var(--radius-200) var(--radius-200) 0 0;
|
|
16
|
+
|
|
17
|
+
> .ds-avatar {
|
|
18
|
+
flex-shrink: 0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.meta {
|
|
22
|
+
display: flex;
|
|
23
|
+
align-items: baseline;
|
|
24
|
+
flex-wrap: wrap;
|
|
25
|
+
gap: var(--spacing-050) var(--spacing-100);
|
|
26
|
+
min-width: 0;
|
|
27
|
+
|
|
28
|
+
.name {
|
|
29
|
+
@include label-medium-1;
|
|
30
|
+
color: var(--text-primary);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.timestamp {
|
|
34
|
+
@include helper-small-1;
|
|
35
|
+
color: var(--text-tertiary);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.buttons {
|
|
40
|
+
display: flex;
|
|
41
|
+
margin-left: auto;
|
|
42
|
+
flex-shrink: 0;
|
|
43
|
+
gap: var(--spacing-100);
|
|
44
|
+
position: relative;
|
|
45
|
+
z-index: 1;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.comment-body {
|
|
50
|
+
padding: var(--spacing-200) var(--spacing-150);
|
|
51
|
+
background-color: var(--background-default);
|
|
52
|
+
border-radius: 0 0 var(--radius-200) var(--radius-200);
|
|
53
|
+
|
|
54
|
+
.text {
|
|
55
|
+
@include body-medium-1;
|
|
56
|
+
color: var(--text-primary);
|
|
57
|
+
word-break: break-word;
|
|
58
|
+
|
|
59
|
+
.wmde-markdown {
|
|
60
|
+
background: transparent;
|
|
61
|
+
font-size: inherit;
|
|
62
|
+
color: inherit;
|
|
63
|
+
|
|
64
|
+
p {
|
|
65
|
+
margin: 0 0 var(--spacing-100);
|
|
66
|
+
|
|
67
|
+
&:last-child {
|
|
68
|
+
margin-bottom: 0;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
pre,
|
|
73
|
+
code {
|
|
74
|
+
border-radius: var(--radius-100);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
pre {
|
|
78
|
+
overflow-x: auto;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
ul,
|
|
82
|
+
ol {
|
|
83
|
+
margin: 0 0 var(--spacing-100);
|
|
84
|
+
padding-left: var(--spacing-250);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.meta-line {
|
|
90
|
+
@include helper-small-1;
|
|
91
|
+
color: var(--text-tertiary);
|
|
92
|
+
margin-top: var(--spacing-100);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -4,6 +4,7 @@ import { Comment, CommentRequest } from './types/Comment';
|
|
|
4
4
|
declare function findNotifications(uid: string, mode: string): Promise<AxiosResponse<Comment[], any, {}, any>>;
|
|
5
5
|
declare function findReadNotifications(uid: string, mode: string): Promise<AxiosResponse<Comment[], any, {}, any>>;
|
|
6
6
|
declare function markRead(id: string): Promise<AxiosResponse<Comment, any, {}, any>>;
|
|
7
|
+
declare function resolve(id: string): Promise<AxiosResponse<Comment, any, {}, any>>;
|
|
7
8
|
declare function update(id: string, comment: CommentRequest): Promise<AxiosResponse<Comment, any, {}, any>>;
|
|
8
9
|
declare function remove(id: string): Promise<AxiosResponse<Comment, any, {}, any>>;
|
|
9
10
|
declare function add(uid: string, mode: string, comment: CommentRequest, references: Record<string, string | number | boolean>, keywords?: Record<string, string | number | boolean>): Promise<AxiosResponse<Comment, any, {}, any>>;
|
|
@@ -14,6 +15,7 @@ declare const CommentService: {
|
|
|
14
15
|
remove: typeof remove;
|
|
15
16
|
update: typeof update;
|
|
16
17
|
markRead: typeof markRead;
|
|
18
|
+
resolve: typeof resolve;
|
|
17
19
|
find: typeof find;
|
|
18
20
|
findNotifications: typeof findNotifications;
|
|
19
21
|
findReadNotifications: typeof findReadNotifications;
|
|
@@ -40,6 +40,13 @@ function markRead(id) {
|
|
|
40
40
|
}
|
|
41
41
|
return api.put(`${import.meta.env.VITE_APP_COMMENT_API_URL}/read/${id}`);
|
|
42
42
|
}
|
|
43
|
+
function resolve(id) {
|
|
44
|
+
if (StorybookUtils.isStorybook()) {
|
|
45
|
+
storybookComments = storybookComments.map((c) => c.id === id ? { ...c, resolved: true } : c);
|
|
46
|
+
return mockResponse(storybookComments.find((c) => c.id === id));
|
|
47
|
+
}
|
|
48
|
+
return api.put(`${import.meta.env.VITE_APP_COMMENT_API_URL}/resolve/${id}`);
|
|
49
|
+
}
|
|
43
50
|
function update(id, comment) {
|
|
44
51
|
if (StorybookUtils.isStorybook()) {
|
|
45
52
|
storybookComments = storybookComments.map((c) => {
|
|
@@ -106,6 +113,7 @@ const CommentService = {
|
|
|
106
113
|
remove,
|
|
107
114
|
update,
|
|
108
115
|
markRead,
|
|
116
|
+
resolve,
|
|
109
117
|
find,
|
|
110
118
|
findNotifications,
|
|
111
119
|
findReadNotifications,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import UsabilityUtils from '
|
|
3
|
-
import CommentInfo from '
|
|
2
|
+
import UsabilityUtils from '../../../functions/UsabilityUtils';
|
|
3
|
+
import CommentInfo from '../CommentInfo';
|
|
4
|
+
import './styles.scss';
|
|
4
5
|
export default function CommentThread({ comments, hasEditAccess, onRemove, onUpdate, onEdit, onReply, }) {
|
|
5
6
|
const hasReplies = (thisComment) => comments.some((c) => c.reply === thisComment.id);
|
|
6
7
|
const getReplies = (thisComment) => comments.filter((c) => c.reply === thisComment.id);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
.thread {
|
|
2
|
+
display: flex;
|
|
3
|
+
flex-direction: column;
|
|
4
|
+
gap: var(--spacing-200);
|
|
5
|
+
|
|
6
|
+
& + .thread {
|
|
7
|
+
margin-top: var(--spacing-100);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.replies {
|
|
11
|
+
margin-left: var(--spacing-400);
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
gap: var(--spacing-100);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -13,6 +13,7 @@ import CommentService from './CommentService';
|
|
|
13
13
|
import CommentThread from './CommentThread';
|
|
14
14
|
import CommentUtils from './CommentUtils';
|
|
15
15
|
import './style.scss';
|
|
16
|
+
import useCommentsResize from './useCommentsResize';
|
|
16
17
|
function CommentsPanel({ dockPosition, showResizeHandle, onResizePointerDown, children, }) {
|
|
17
18
|
const isHorizontal = dockPosition === 'bottom';
|
|
18
19
|
return (_jsxs("div", { className: cx('comments-container', {
|
|
@@ -33,9 +34,8 @@ export default function Comments({ uid, keywords, references, mode, onClose, onC
|
|
|
33
34
|
const [comments, setComments] = useState();
|
|
34
35
|
const [users, setUsers] = useState([]);
|
|
35
36
|
const [reply, setReply] = useState();
|
|
36
|
-
const [drawerWidth, setDrawerWidth] = useState(CommentUtils.readStoredDrawerWidth);
|
|
37
|
-
const [drawerHeight, setDrawerHeight] = useState(CommentUtils.readStoredDrawerHeight);
|
|
38
37
|
const [dockPosition, setDockPosition] = useState(CommentUtils.readStoredDockPosition);
|
|
38
|
+
const { drawerWidth, drawerHeight, handleResizePointerDown } = useCommentsResize(dockPosition);
|
|
39
39
|
const [externalContainer, setExternalContainer] = useState(null);
|
|
40
40
|
const commentAddRef = useRef(null);
|
|
41
41
|
const popupRef = useRef(null);
|
|
@@ -209,55 +209,6 @@ export default function Comments({ uid, keywords, references, mode, onClose, onC
|
|
|
209
209
|
closePopup();
|
|
210
210
|
};
|
|
211
211
|
}, [closePopup]);
|
|
212
|
-
const handleResizePointerDown = useCallback((event) => {
|
|
213
|
-
event.preventDefault();
|
|
214
|
-
const handle = event.currentTarget;
|
|
215
|
-
handle.setPointerCapture(event.pointerId);
|
|
216
|
-
const previousUserSelect = document.body.style.userSelect;
|
|
217
|
-
const previousCursor = document.body.style.cursor;
|
|
218
|
-
const isBottom = dockPosition === 'bottom';
|
|
219
|
-
const isLeft = dockPosition === 'left';
|
|
220
|
-
document.body.style.userSelect = 'none';
|
|
221
|
-
document.body.style.cursor = isBottom ? 'row-resize' : 'col-resize';
|
|
222
|
-
const computeSize = (clientX, clientY) => {
|
|
223
|
-
if (isBottom) {
|
|
224
|
-
return CommentUtils.clampDrawerHeight(window.innerHeight - clientY);
|
|
225
|
-
}
|
|
226
|
-
if (isLeft) {
|
|
227
|
-
return CommentUtils.clampDrawerWidth(clientX);
|
|
228
|
-
}
|
|
229
|
-
return CommentUtils.clampDrawerWidth(window.innerWidth - clientX);
|
|
230
|
-
};
|
|
231
|
-
const onPointerMove = (moveEvent) => {
|
|
232
|
-
const next = computeSize(moveEvent.clientX, moveEvent.clientY);
|
|
233
|
-
if (isBottom) {
|
|
234
|
-
setDrawerHeight(next);
|
|
235
|
-
}
|
|
236
|
-
else {
|
|
237
|
-
setDrawerWidth(next);
|
|
238
|
-
}
|
|
239
|
-
};
|
|
240
|
-
const onPointerUp = (upEvent) => {
|
|
241
|
-
const next = computeSize(upEvent.clientX, upEvent.clientY);
|
|
242
|
-
if (isBottom) {
|
|
243
|
-
setDrawerHeight(next);
|
|
244
|
-
CommentUtils.persistDrawerHeight(next);
|
|
245
|
-
}
|
|
246
|
-
else {
|
|
247
|
-
setDrawerWidth(next);
|
|
248
|
-
CommentUtils.persistDrawerWidth(next);
|
|
249
|
-
}
|
|
250
|
-
document.body.style.userSelect = previousUserSelect;
|
|
251
|
-
document.body.style.cursor = previousCursor;
|
|
252
|
-
handle.releasePointerCapture(upEvent.pointerId);
|
|
253
|
-
handle.removeEventListener('pointermove', onPointerMove);
|
|
254
|
-
handle.removeEventListener('pointerup', onPointerUp);
|
|
255
|
-
handle.removeEventListener('pointercancel', onPointerUp);
|
|
256
|
-
};
|
|
257
|
-
handle.addEventListener('pointermove', onPointerMove);
|
|
258
|
-
handle.addEventListener('pointerup', onPointerUp);
|
|
259
|
-
handle.addEventListener('pointercancel', onPointerUp);
|
|
260
|
-
}, [dockPosition]);
|
|
261
212
|
const paperStyle = dockPosition === 'bottom'
|
|
262
213
|
? { height: drawerHeight, width: '100%' }
|
|
263
214
|
: { width: drawerWidth };
|
|
@@ -123,116 +123,6 @@
|
|
|
123
123
|
overflow-y: auto;
|
|
124
124
|
padding: var(--spacing-200);
|
|
125
125
|
background-color: var(--background-default);
|
|
126
|
-
|
|
127
|
-
.thread {
|
|
128
|
-
display: flex;
|
|
129
|
-
flex-direction: column;
|
|
130
|
-
gap: var(--spacing-200);
|
|
131
|
-
|
|
132
|
-
& + .thread {
|
|
133
|
-
margin-top: var(--spacing-100);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
.comment {
|
|
137
|
-
display: flex;
|
|
138
|
-
flex-direction: column;
|
|
139
|
-
border: 1px solid var(--border-subtle-1);
|
|
140
|
-
border-radius: var(--radius-200);
|
|
141
|
-
overflow: hidden;
|
|
142
|
-
background-color: var(--background-default);
|
|
143
|
-
|
|
144
|
-
.comment-header {
|
|
145
|
-
display: flex;
|
|
146
|
-
align-items: center;
|
|
147
|
-
gap: var(--spacing-100);
|
|
148
|
-
padding: 4px 8px;
|
|
149
|
-
background-color: var(--layer-hover-2);
|
|
150
|
-
border-bottom: 1px solid var(--border-subtle-1);
|
|
151
|
-
|
|
152
|
-
> .ds-avatar {
|
|
153
|
-
flex-shrink: 0;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
.meta {
|
|
157
|
-
display: flex;
|
|
158
|
-
align-items: baseline;
|
|
159
|
-
flex-wrap: wrap;
|
|
160
|
-
gap: var(--spacing-050) var(--spacing-100);
|
|
161
|
-
min-width: 0;
|
|
162
|
-
|
|
163
|
-
.name {
|
|
164
|
-
@include label-medium-1;
|
|
165
|
-
color: var(--text-primary);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
.timestamp {
|
|
169
|
-
@include helper-small-1;
|
|
170
|
-
color: var(--text-tertiary);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
.buttons {
|
|
175
|
-
display: flex;
|
|
176
|
-
margin-left: auto;
|
|
177
|
-
flex-shrink: 0;
|
|
178
|
-
gap: var(--spacing-100);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
.comment-body {
|
|
183
|
-
padding: var(--spacing-200) var(--spacing-150);
|
|
184
|
-
background-color: var(--background-default);
|
|
185
|
-
|
|
186
|
-
.text {
|
|
187
|
-
@include body-medium-1;
|
|
188
|
-
color: var(--text-primary);
|
|
189
|
-
word-break: break-word;
|
|
190
|
-
|
|
191
|
-
.wmde-markdown {
|
|
192
|
-
background: transparent;
|
|
193
|
-
font-size: inherit;
|
|
194
|
-
color: inherit;
|
|
195
|
-
|
|
196
|
-
p {
|
|
197
|
-
margin: 0 0 var(--spacing-100);
|
|
198
|
-
|
|
199
|
-
&:last-child {
|
|
200
|
-
margin-bottom: 0;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
pre,
|
|
205
|
-
code {
|
|
206
|
-
border-radius: var(--radius-100);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
pre {
|
|
210
|
-
overflow-x: auto;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
ul,
|
|
214
|
-
ol {
|
|
215
|
-
margin: 0 0 var(--spacing-100);
|
|
216
|
-
padding-left: var(--spacing-250);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
.meta-line {
|
|
222
|
-
@include helper-small-1;
|
|
223
|
-
color: var(--text-tertiary);
|
|
224
|
-
margin-top: var(--spacing-100);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
.replies {
|
|
230
|
-
margin-left: var(--spacing-400);
|
|
231
|
-
display: flex;
|
|
232
|
-
flex-direction: column;
|
|
233
|
-
gap: var(--spacing-100);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
126
|
}
|
|
237
127
|
}
|
|
238
128
|
|
|
@@ -9,6 +9,7 @@ export type Comment = {
|
|
|
9
9
|
value: string;
|
|
10
10
|
date: string;
|
|
11
11
|
edited?: string;
|
|
12
|
+
resolved?: boolean;
|
|
12
13
|
name: string;
|
|
13
14
|
user: string;
|
|
14
15
|
picture: string;
|
|
@@ -32,6 +33,7 @@ export type UserComment = {
|
|
|
32
33
|
picture: string;
|
|
33
34
|
email: string;
|
|
34
35
|
read: boolean;
|
|
36
|
+
dateRead?: string;
|
|
35
37
|
};
|
|
36
38
|
export declare const EMPTY_COMMENT: Comment;
|
|
37
39
|
export declare const EMPTY_COMMENT_REQUEST: CommentRequest;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type PointerEvent as ReactPointerEvent } from 'react';
|
|
2
|
+
import { type DockPosition } from './CommentUtils';
|
|
3
|
+
export default function useCommentsResize(dockPosition: DockPosition): {
|
|
4
|
+
drawerWidth: number;
|
|
5
|
+
drawerHeight: number;
|
|
6
|
+
handleResizePointerDown: (event: ReactPointerEvent<HTMLDivElement>) => void;
|
|
7
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { useCallback, useState, } from 'react';
|
|
2
|
+
import CommentUtils from './CommentUtils';
|
|
3
|
+
export default function useCommentsResize(dockPosition) {
|
|
4
|
+
const [drawerWidth, setDrawerWidth] = useState(CommentUtils.readStoredDrawerWidth);
|
|
5
|
+
const [drawerHeight, setDrawerHeight] = useState(CommentUtils.readStoredDrawerHeight);
|
|
6
|
+
const handleResizePointerDown = useCallback((event) => {
|
|
7
|
+
event.preventDefault();
|
|
8
|
+
const handle = event.currentTarget;
|
|
9
|
+
handle.setPointerCapture(event.pointerId);
|
|
10
|
+
const previousUserSelect = document.body.style.userSelect;
|
|
11
|
+
const previousCursor = document.body.style.cursor;
|
|
12
|
+
const isBottom = dockPosition === 'bottom';
|
|
13
|
+
const isLeft = dockPosition === 'left';
|
|
14
|
+
document.body.style.userSelect = 'none';
|
|
15
|
+
document.body.style.cursor = isBottom ? 'row-resize' : 'col-resize';
|
|
16
|
+
const computeSize = (clientX, clientY) => {
|
|
17
|
+
if (isBottom) {
|
|
18
|
+
return CommentUtils.clampDrawerHeight(window.innerHeight - clientY);
|
|
19
|
+
}
|
|
20
|
+
if (isLeft) {
|
|
21
|
+
return CommentUtils.clampDrawerWidth(clientX);
|
|
22
|
+
}
|
|
23
|
+
return CommentUtils.clampDrawerWidth(window.innerWidth - clientX);
|
|
24
|
+
};
|
|
25
|
+
const onPointerMove = (moveEvent) => {
|
|
26
|
+
const next = computeSize(moveEvent.clientX, moveEvent.clientY);
|
|
27
|
+
if (isBottom) {
|
|
28
|
+
setDrawerHeight(next);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
setDrawerWidth(next);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
const onPointerUp = (upEvent) => {
|
|
35
|
+
const next = computeSize(upEvent.clientX, upEvent.clientY);
|
|
36
|
+
if (isBottom) {
|
|
37
|
+
setDrawerHeight(next);
|
|
38
|
+
CommentUtils.persistDrawerHeight(next);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
setDrawerWidth(next);
|
|
42
|
+
CommentUtils.persistDrawerWidth(next);
|
|
43
|
+
}
|
|
44
|
+
document.body.style.userSelect = previousUserSelect;
|
|
45
|
+
document.body.style.cursor = previousCursor;
|
|
46
|
+
handle.releasePointerCapture(upEvent.pointerId);
|
|
47
|
+
handle.removeEventListener('pointermove', onPointerMove);
|
|
48
|
+
handle.removeEventListener('pointerup', onPointerUp);
|
|
49
|
+
handle.removeEventListener('pointercancel', onPointerUp);
|
|
50
|
+
};
|
|
51
|
+
handle.addEventListener('pointermove', onPointerMove);
|
|
52
|
+
handle.addEventListener('pointerup', onPointerUp);
|
|
53
|
+
handle.addEventListener('pointercancel', onPointerUp);
|
|
54
|
+
}, [dockPosition]);
|
|
55
|
+
return {
|
|
56
|
+
drawerWidth,
|
|
57
|
+
drawerHeight,
|
|
58
|
+
handleResizePointerDown,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -109,8 +109,9 @@ export function shouldSkipJunctionContinuationElement(elements, index, selectedT
|
|
|
109
109
|
if (element.type === PageRenderElementType.JUNCTION_SPLIT &&
|
|
110
110
|
splitIdx > 0 &&
|
|
111
111
|
segmented) {
|
|
112
|
-
|
|
113
|
-
|
|
112
|
+
// Only hand later splits to a later host when that host also has junction
|
|
113
|
+
// (two-host diplomatic pattern). Seg-only follow-ons must not hide idx>0.
|
|
114
|
+
if (hasLaterJunctionHostInSegmentationGroup(elements, ref.uid, segmentationGroup, ref.p)) {
|
|
114
115
|
return true;
|
|
115
116
|
}
|
|
116
117
|
}
|
|
@@ -196,6 +197,30 @@ function getLastSegmentationPosInGroup(elements, start, end) {
|
|
|
196
197
|
}
|
|
197
198
|
return lastP;
|
|
198
199
|
}
|
|
200
|
+
function groupHasJunctionHost(elements, start, end) {
|
|
201
|
+
for (let i = start; i <= end; i += 1) {
|
|
202
|
+
const ref = elements[i].token;
|
|
203
|
+
if (ref && hasJunctionFromRef(ref)) {
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
function hasLaterJunctionHostInSegmentationGroup(elements, uid, groupKey, currentP) {
|
|
210
|
+
for (const element of elements) {
|
|
211
|
+
if (!isJunctionEditionElement(element))
|
|
212
|
+
continue;
|
|
213
|
+
const ref = element.token;
|
|
214
|
+
if (!sameSegmentationGroupRef(ref, uid, groupKey))
|
|
215
|
+
continue;
|
|
216
|
+
if (!hasSegmentationFromRef(ref))
|
|
217
|
+
continue;
|
|
218
|
+
if (ref.p > currentP) {
|
|
219
|
+
return true;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
199
224
|
export function shouldSkipSegmentationElement(elements, index, selectedTier, corpus) {
|
|
200
225
|
if (!isUnderSelected(selectedTier, SEGMENTATION_TIER, corpus)) {
|
|
201
226
|
return false;
|
|
@@ -205,19 +230,99 @@ export function shouldSkipSegmentationElement(elements, index, selectedTier, cor
|
|
|
205
230
|
return false;
|
|
206
231
|
}
|
|
207
232
|
const { start, end } = findSegmentationGroupBounds(elements, index);
|
|
233
|
+
const ref = element.token;
|
|
234
|
+
// Junction host + seg-only pieces: diplomatic text lives on the junction host.
|
|
235
|
+
if (groupHasJunctionHost(elements, start, end)) {
|
|
236
|
+
if (hasJunctionFromRef(ref)) {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
208
241
|
const lastP = getLastSegmentationPosInGroup(elements, start, end);
|
|
209
|
-
return
|
|
242
|
+
return ref.p < lastP;
|
|
210
243
|
}
|
|
211
244
|
/** @deprecated Use shouldSkipSegmentationElement */
|
|
212
245
|
export function shouldSkipSegmentationToken(elements, index, selectedTier, corpus) {
|
|
213
246
|
return shouldSkipSegmentationElement(elements, index, selectedTier, corpus);
|
|
214
247
|
}
|
|
248
|
+
function isMidJunctionHostLineBreak(elements, index) {
|
|
249
|
+
if (elements[index]?.type !== PageRenderElementType.LINE_BREAK) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
const prev = elements[index - 1];
|
|
253
|
+
if (prev?.type !== PageRenderElementType.JUNCTION_SPLIT || !prev.token) {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
let nextIdx = index + 1;
|
|
257
|
+
while (nextIdx < elements.length &&
|
|
258
|
+
elements[nextIdx].type === PageRenderElementType.LINE_NUMBER) {
|
|
259
|
+
nextIdx += 1;
|
|
260
|
+
}
|
|
261
|
+
const next = elements[nextIdx];
|
|
262
|
+
if (next?.type !== PageRenderElementType.JUNCTION_SPLIT || !next.token) {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
return prev.token.uid === next.token.uid && prev.token.p === next.token.p;
|
|
266
|
+
}
|
|
267
|
+
function sameDeferredBreakGroup(element, group) {
|
|
268
|
+
const ref = element.token;
|
|
269
|
+
if (!ref)
|
|
270
|
+
return false;
|
|
271
|
+
if (ref.uid === group.uid && ref.p === group.p)
|
|
272
|
+
return true;
|
|
273
|
+
if (group.segGroup &&
|
|
274
|
+
hasSegmentationFromRef(ref) &&
|
|
275
|
+
sameSegmentationGroupRef(ref, group.uid, group.segGroup)) {
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
215
280
|
export default function collapseElementsForTier(elements, selectedTier, corpus) {
|
|
216
281
|
const showJunctionSplits = isUnderSelected(selectedTier, JUNCTION_TIER, corpus);
|
|
217
282
|
const collapsed = [];
|
|
218
283
|
let index = 0;
|
|
284
|
+
let deferredBreak = null;
|
|
285
|
+
let deferredLineNumbers = [];
|
|
286
|
+
let deferredBreakGroup = null;
|
|
287
|
+
const flushDeferredBreak = () => {
|
|
288
|
+
if (!deferredBreak)
|
|
289
|
+
return;
|
|
290
|
+
collapsed.push({
|
|
291
|
+
kind: 'single',
|
|
292
|
+
element: deferredBreak,
|
|
293
|
+
key: `LINE_BREAK_deferred_${collapsed.length}`,
|
|
294
|
+
});
|
|
295
|
+
for (const lineNumber of deferredLineNumbers) {
|
|
296
|
+
collapsed.push({
|
|
297
|
+
kind: 'single',
|
|
298
|
+
element: lineNumber,
|
|
299
|
+
key: `LINE_NUMBER_deferred_${collapsed.length}`,
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
deferredBreak = null;
|
|
303
|
+
deferredLineNumbers = [];
|
|
304
|
+
deferredBreakGroup = null;
|
|
305
|
+
};
|
|
219
306
|
while (index < elements.length) {
|
|
220
307
|
const element = elements[index];
|
|
308
|
+
if (!showJunctionSplits &&
|
|
309
|
+
deferredBreak === null &&
|
|
310
|
+
isMidJunctionHostLineBreak(elements, index)) {
|
|
311
|
+
const host = elements[index - 1].token;
|
|
312
|
+
deferredBreak = element;
|
|
313
|
+
deferredBreakGroup = {
|
|
314
|
+
uid: host.uid,
|
|
315
|
+
p: host.p,
|
|
316
|
+
segGroup: segmentationGroupKey(host),
|
|
317
|
+
};
|
|
318
|
+
index += 1;
|
|
319
|
+
while (index < elements.length &&
|
|
320
|
+
elements[index].type === PageRenderElementType.LINE_NUMBER) {
|
|
321
|
+
deferredLineNumbers.push(elements[index]);
|
|
322
|
+
index += 1;
|
|
323
|
+
}
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
221
326
|
if (shouldSkipSegmentationElement(elements, index, selectedTier, corpus)) {
|
|
222
327
|
index += 1;
|
|
223
328
|
continue;
|
|
@@ -226,6 +331,12 @@ export default function collapseElementsForTier(elements, selectedTier, corpus)
|
|
|
226
331
|
index += 1;
|
|
227
332
|
continue;
|
|
228
333
|
}
|
|
334
|
+
const inDeferredGroup = !!element.token &&
|
|
335
|
+
!!deferredBreakGroup &&
|
|
336
|
+
sameDeferredBreakGroup(element, deferredBreakGroup);
|
|
337
|
+
if (deferredBreak && !inDeferredGroup) {
|
|
338
|
+
flushDeferredBreak();
|
|
339
|
+
}
|
|
229
340
|
if (!showJunctionSplits &&
|
|
230
341
|
element.type === PageRenderElementType.JUNCTION_SPLIT &&
|
|
231
342
|
element.token) {
|
|
@@ -251,6 +362,7 @@ export default function collapseElementsForTier(elements, selectedTier, corpus)
|
|
|
251
362
|
});
|
|
252
363
|
index += 1;
|
|
253
364
|
}
|
|
365
|
+
flushDeferredBreak();
|
|
254
366
|
return collapsed;
|
|
255
367
|
}
|
|
256
368
|
export function isTokenBearingCollapsed(item) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tycho-components",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.42.
|
|
4
|
+
"version": "0.42.2",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"exports": {
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"react-i18next": "^13.0.2",
|
|
86
86
|
"react-router-dom": "^6.14.2",
|
|
87
87
|
"react-toastify": "^9.1.3",
|
|
88
|
-
"tycho-storybook": "0.12.
|
|
88
|
+
"tycho-storybook": "0.12.1",
|
|
89
89
|
"wavesurfer-react": "^2.2.2",
|
|
90
90
|
"wavesurfer.js": "^6.6.3"
|
|
91
91
|
},
|
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
"react-toastify": "^9.1.3",
|
|
116
116
|
"sass-embedded": "^1.97.2",
|
|
117
117
|
"storybook": "^10.1.11",
|
|
118
|
-
"tycho-storybook": "^0.12.
|
|
118
|
+
"tycho-storybook": "^0.12.1",
|
|
119
119
|
"typescript": "^5.7.3",
|
|
120
120
|
"vite": "^7.0.0",
|
|
121
121
|
"vitest": "^3.2.6",
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import MDEditor from '@uiw/react-md-editor';
|
|
3
|
-
import '@uiw/react-markdown-preview/markdown.css';
|
|
4
|
-
import { useContext } from 'react';
|
|
5
|
-
import { useTranslation } from 'react-i18next';
|
|
6
|
-
import { Avatar, IconButton, Tag } from 'tycho-storybook';
|
|
7
|
-
import CommonContext from '../../configs/CommonContext';
|
|
8
|
-
import DateUtils from '../../functions/DateUtils';
|
|
9
|
-
import CommentService from './CommentService';
|
|
10
|
-
import './style.scss';
|
|
11
|
-
export default function CommentInfo({ comment, hasEditAccess = true, onRemove, onUpdate, onEdit, onReply, }) {
|
|
12
|
-
const { t } = useTranslation('comments');
|
|
13
|
-
const { state } = useContext(CommonContext);
|
|
14
|
-
const handleMarkRead = () => {
|
|
15
|
-
CommentService.markRead(comment.id).then((r) => {
|
|
16
|
-
onUpdate(r.data);
|
|
17
|
-
});
|
|
18
|
-
};
|
|
19
|
-
const handleRemove = () => {
|
|
20
|
-
CommentService.remove(comment.id).then(() => {
|
|
21
|
-
onRemove();
|
|
22
|
-
});
|
|
23
|
-
};
|
|
24
|
-
const isNotRead = (thisComment) => {
|
|
25
|
-
if (!thisComment.requested)
|
|
26
|
-
return false;
|
|
27
|
-
const requested = thisComment.requested.find((r) => r.uid === state.logged?.uid);
|
|
28
|
-
if (!requested)
|
|
29
|
-
return false;
|
|
30
|
-
return !requested.read;
|
|
31
|
-
};
|
|
32
|
-
const displayDate = comment.edited || comment.date;
|
|
33
|
-
const fullDateTitle = DateUtils.formatDateTime(displayDate, 'MM/dd/yyyy - HH:mm');
|
|
34
|
-
return (_jsxs("div", { className: "comment", children: [_jsxs("div", { className: "comment-header", children: [_jsx(Avatar, { title: comment.name, src: comment.picture, size: "x-small" }), _jsxs("div", { className: "meta", children: [_jsx("span", { className: "name", children: comment.name }), _jsxs("span", { className: "timestamp", title: fullDateTitle, children: [t('label.on'), ' ', DateUtils.formatDateTime(displayDate, 'MMM d')] })] }), _jsxs("div", { className: "buttons", children: [comment.edited && (_jsx(Tag, { text: t('label.edited'), color: "blue", size: "small" })), hasEditAccess && !comment.reply && (_jsx(IconButton, { size: "x-small", onClick: onReply, title: t('tooltip.reply'), name: "reply" })), hasEditAccess && state.logged?.uid === comment.user && (_jsxs(_Fragment, { children: [_jsx(IconButton, { size: "x-small", onClick: onEdit, title: t('tooltip.edit'), name: "edit" }), _jsx(IconButton, { size: "x-small", onClick: handleRemove, title: t('tooltip.delete'), name: "delete" })] })), isNotRead(comment) && (_jsx(IconButton, { size: "x-small", onClick: handleMarkRead, title: t('tooltip.read'), name: "check_circle" }))] })] }), _jsxs("div", { className: "comment-body", children: [_jsx("div", { className: "text", "data-color-mode": "light", children: _jsx(MDEditor.Markdown, { source: comment.value }) }), comment.requested && (_jsxs("div", { className: "meta-line", children: [_jsx("span", { className: "me-1", children: t('label.requested.to') }), _jsx("span", { children: comment.requested.map((e) => e.name).join(', ') })] }))] })] }));
|
|
35
|
-
}
|