tycho-components 0.42.1 → 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.
Files changed (27) hide show
  1. package/dist/configs/localization/CommentsTexts.d.ts +30 -0
  2. package/dist/configs/localization/CommentsTexts.js +30 -0
  3. package/dist/features/Comments/CommentDetails/CommentDetails.d.ts +8 -0
  4. package/dist/features/Comments/CommentDetails/CommentDetails.js +15 -0
  5. package/dist/features/Comments/CommentDetails/index.d.ts +2 -0
  6. package/dist/features/Comments/CommentDetails/index.js +2 -0
  7. package/dist/features/Comments/CommentDetails/styles.scss +87 -0
  8. package/dist/features/Comments/{CommentInfo.d.ts → CommentInfo/CommentInfo.d.ts} +2 -2
  9. package/dist/features/Comments/CommentInfo/CommentInfo.js +74 -0
  10. package/dist/features/Comments/CommentInfo/index.d.ts +2 -0
  11. package/dist/features/Comments/CommentInfo/index.js +2 -0
  12. package/dist/features/Comments/CommentInfo/styles.scss +95 -0
  13. package/dist/features/Comments/CommentService.d.ts +2 -0
  14. package/dist/features/Comments/CommentService.js +8 -0
  15. package/dist/features/Comments/CommentService.mock.js +1 -0
  16. package/dist/features/Comments/{CommentThread.d.ts → CommentThread/CommentThread.d.ts} +2 -1
  17. package/dist/features/Comments/{CommentThread.js → CommentThread/CommentThread.js} +3 -2
  18. package/dist/features/Comments/CommentThread/index.d.ts +2 -0
  19. package/dist/features/Comments/CommentThread/index.js +2 -0
  20. package/dist/features/Comments/CommentThread/styles.scss +16 -0
  21. package/dist/features/Comments/Comments.js +2 -51
  22. package/dist/features/Comments/style.scss +0 -110
  23. package/dist/features/Comments/types/Comment.d.ts +2 -0
  24. package/dist/features/Comments/useCommentsResize.d.ts +7 -0
  25. package/dist/features/Comments/useCommentsResize.js +60 -0
  26. package/package.json +3 -3
  27. 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,8 @@
1
+ import { Comment } from '../types/Comment';
2
+ import './styles.scss';
3
+ type Props = {
4
+ comment: Comment;
5
+ onClose: () => void;
6
+ };
7
+ export default function CommentDetails({ comment, onClose }: Props): import("react").JSX.Element;
8
+ export {};
@@ -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,2 @@
1
+ import CommentDetails from './CommentDetails';
2
+ export default CommentDetails;
@@ -0,0 +1,2 @@
1
+ import CommentDetails from './CommentDetails';
2
+ export default CommentDetails;
@@ -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
+ }
@@ -1,6 +1,6 @@
1
1
  import '@uiw/react-markdown-preview/markdown.css';
2
- import './style.scss';
3
- import { Comment } from './types/Comment';
2
+ import { Comment } from '../types/Comment';
3
+ import './styles.scss';
4
4
  type Props = {
5
5
  comment: Comment;
6
6
  hasEditAccess?: boolean;
@@ -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,2 @@
1
+ import CommentInfo from './CommentInfo';
2
+ export default CommentInfo;
@@ -0,0 +1,2 @@
1
+ import CommentInfo from './CommentInfo';
2
+ export default CommentInfo;
@@ -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,
@@ -37,6 +37,7 @@ export const mockComments = [
37
37
  user: 'storybook-user-ana',
38
38
  picture: '',
39
39
  edited: new Date(Date.now() - 86400000).toISOString(),
40
+ resolved: true,
40
41
  requested: [
41
42
  {
42
43
  uid: 'storybook-user-john',
@@ -1,4 +1,5 @@
1
- import { Comment } from './types/Comment';
1
+ import { Comment } from '../types/Comment';
2
+ import './styles.scss';
2
3
  type Props = {
3
4
  comments: Comment[];
4
5
  hasEditAccess: boolean;
@@ -1,6 +1,7 @@
1
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';
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,2 @@
1
+ import CommentThread from './CommentThread';
2
+ export default CommentThread;
@@ -0,0 +1,2 @@
1
+ import CommentThread from './CommentThread';
2
+ export default CommentThread;
@@ -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
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tycho-components",
3
3
  "private": false,
4
- "version": "0.42.1",
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.0",
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.0",
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
- }