@selfcommunity/react-ui 0.10.2-courses.159 → 0.10.2-courses.161
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/lib/cjs/components/CommentObjectReply/CommentObjectReply.js +11 -5
- package/lib/cjs/components/Composer/Content/ContentLesson/ContentLesson.js +35 -4
- package/lib/cjs/components/CourseDashboard/Teacher/Comments.js +2 -2
- package/lib/cjs/components/CourseParticipantsButton/CourseParticipantsButton.js +1 -1
- package/lib/cjs/components/Editor/Editor.d.ts +13 -1
- package/lib/cjs/components/Editor/Editor.js +3 -4
- package/lib/cjs/components/Editor/nodes/index.d.ts +1 -2
- package/lib/cjs/components/Editor/nodes/index.js +1 -3
- package/lib/cjs/components/Editor/plugins/MediaPlugin.d.ts +2 -1
- package/lib/cjs/components/Editor/plugins/MediaPlugin.js +5 -27
- package/lib/cjs/components/Editor/plugins/ToolbarPlugin.d.ts +2 -0
- package/lib/cjs/components/Editor/plugins/ToolbarPlugin.js +2 -2
- package/lib/cjs/components/LessonCommentObject/LessonCommentObject.d.ts +1 -0
- package/lib/cjs/components/LessonCommentObject/LessonCommentObject.js +8 -2
- package/lib/cjs/components/LessonCommentObjects/LessonCommentObjects.js +5 -1
- package/lib/cjs/components/LessonObject/LessonObject.js +7 -3
- package/lib/cjs/shared/AccordionLessons/AccordionLessons.js +5 -4
- package/lib/cjs/shared/LessonFilePreview/index.d.ts +12 -0
- package/lib/cjs/shared/LessonFilePreview/index.js +29 -0
- package/lib/cjs/shared/Media/Link/UrlTextField/index.js +2 -3
- package/lib/esm/components/CommentObjectReply/CommentObjectReply.js +11 -5
- package/lib/esm/components/Composer/Content/ContentLesson/ContentLesson.js +37 -6
- package/lib/esm/components/CourseDashboard/Teacher/Comments.js +3 -3
- package/lib/esm/components/CourseParticipantsButton/CourseParticipantsButton.js +1 -1
- package/lib/esm/components/Editor/Editor.d.ts +13 -1
- package/lib/esm/components/Editor/Editor.js +3 -4
- package/lib/esm/components/Editor/nodes/index.d.ts +1 -2
- package/lib/esm/components/Editor/nodes/index.js +1 -3
- package/lib/esm/components/Editor/plugins/MediaPlugin.d.ts +2 -1
- package/lib/esm/components/Editor/plugins/MediaPlugin.js +5 -27
- package/lib/esm/components/Editor/plugins/ToolbarPlugin.d.ts +2 -0
- package/lib/esm/components/Editor/plugins/ToolbarPlugin.js +2 -2
- package/lib/esm/components/LessonCommentObject/LessonCommentObject.d.ts +1 -0
- package/lib/esm/components/LessonCommentObject/LessonCommentObject.js +8 -2
- package/lib/esm/components/LessonCommentObjects/LessonCommentObjects.js +5 -1
- package/lib/esm/components/LessonObject/LessonObject.js +8 -4
- package/lib/esm/shared/AccordionLessons/AccordionLessons.js +6 -5
- package/lib/esm/shared/LessonFilePreview/index.d.ts +12 -0
- package/lib/esm/shared/LessonFilePreview/index.js +25 -0
- package/lib/esm/shared/Media/Link/UrlTextField/index.js +3 -4
- package/lib/umd/react-ui.js +1 -1
- package/package.json +8 -8
- package/lib/cjs/components/Editor/nodes/DocNode.d.ts +0 -39
- package/lib/cjs/components/Editor/nodes/DocNode.js +0 -181
- package/lib/esm/components/Editor/nodes/DocNode.d.ts +0 -39
- package/lib/esm/components/Editor/nodes/DocNode.js +0 -175
|
@@ -12,10 +12,12 @@ import { LoadingButton } from '@mui/lab';
|
|
|
12
12
|
import BaseItem from '../../shared/BaseItem';
|
|
13
13
|
import UserAvatar from '../../shared/UserAvatar';
|
|
14
14
|
import { useThemeProps } from '@mui/system';
|
|
15
|
+
import PreviewComponent from '../../shared/Media/File/PreviewComponent';
|
|
15
16
|
const PREFIX = 'SCCommentObjectReply';
|
|
16
17
|
const classes = {
|
|
17
18
|
root: `${PREFIX}-root`,
|
|
18
19
|
comment: `${PREFIX}-comment`,
|
|
20
|
+
media: `${PREFIX}-media`,
|
|
19
21
|
hasValue: `${PREFIX}-has-value`,
|
|
20
22
|
avatar: `${PREFIX}-avatar`,
|
|
21
23
|
actions: `${PREFIX}-actions`,
|
|
@@ -68,7 +70,8 @@ export default function CommentObjectReply(inProps) {
|
|
|
68
70
|
const scUserContext = useSCUser();
|
|
69
71
|
// RETRIEVE OBJECTS
|
|
70
72
|
const [html, setHtml] = useState(text);
|
|
71
|
-
const [media, setMedia] = useState(medias);
|
|
73
|
+
const [media, setMedia] = useState(medias !== null && medias !== void 0 ? medias : []);
|
|
74
|
+
const [uploadingMedia, setUploadingMedia] = useState(false);
|
|
72
75
|
// HOOKS
|
|
73
76
|
const theme = useTheme();
|
|
74
77
|
const isMobile = useMediaQuery(theme.breakpoints.down('md'));
|
|
@@ -113,8 +116,11 @@ export default function CommentObjectReply(inProps) {
|
|
|
113
116
|
const handleChangeText = (value) => {
|
|
114
117
|
setHtml(value);
|
|
115
118
|
};
|
|
116
|
-
const handleChangeMedia = (
|
|
117
|
-
setMedia(
|
|
119
|
+
const handleChangeMedia = (value) => {
|
|
120
|
+
setMedia((prev) => [...prev, value]);
|
|
121
|
+
};
|
|
122
|
+
const handleChangeMedias = (value) => {
|
|
123
|
+
setMedia([...value]);
|
|
118
124
|
};
|
|
119
125
|
/**
|
|
120
126
|
* Check if editor is empty
|
|
@@ -125,6 +131,6 @@ export default function CommentObjectReply(inProps) {
|
|
|
125
131
|
}, [html]);
|
|
126
132
|
// RENDER
|
|
127
133
|
return (_jsx(Root, Object.assign({ id: id }, rest, { disableTypography: true, onClick: handleEditorFocus, elevation: elevation, className: classNames(classes.root, className), image: showAvatar &&
|
|
128
|
-
(!scUserContext.user ? (_jsx(Avatar, { variant: "circular", className: classes.avatar })) : (_jsx(UserAvatar, Object.assign({ hide: !scUserContext.user.community_badge }, { children: _jsx(Avatar, { alt: scUserContext.user.username, variant: "circular", src: scUserContext.user.avatar, classes: { root: classes.avatar } }) })))), secondary: _jsxs(Widget, Object.assign({ className: classNames(classes.comment, { [classes.hasValue]: !isEditorEmpty }) }, WidgetProps, { children: [_jsx(Editor, Object.assign({ ref: editor, onChange: handleChangeText,
|
|
129
|
-
onReply && (_jsx(IconButton, Object.assign({ onClick: handleReply, className: classes.iconReply }, { children: _jsx(Icon, { children: "send" }) }))) }, EditorProps)), !isEditorEmpty && (_jsxs(Stack, Object.assign({ direction: "row", spacing: 2, className: classes.actions }, { children: [onReply && !replyIcon && (_jsx(LoadingButton, Object.assign({ variant: "outlined", size: "small", onClick: handleReply, loading: !editable, className: classes.buttonReply }, { children: _jsx(FormattedMessage, { id: "ui.commentObject.replyComment.reply", defaultMessage: "ui.commentObject.replyComment.reply" }) }))), onSave && (_jsxs(_Fragment, { children: [onCancel && (_jsx(LoadingButton, Object.assign({ variant: 'text', size: "small", onClick: handleCancel, disabled: !editable, color: "inherit", className: classes.buttonCancel }, { children: _jsx(FormattedMessage, { id: "ui.commentObject.replyComment.cancel", defaultMessage: "ui.commentObject.replyComment.cancel" }) }))), _jsx(LoadingButton, Object.assign({ variant: "outlined", size: "small", onClick: handleSave, loading: !editable, className: classes.buttonSave }, { children: _jsx(FormattedMessage, { id: "ui.commentObject.replyComment.save", defaultMessage: "ui.commentObject.replyComment.save" }) }))] }))] })))] })) })));
|
|
134
|
+
(!scUserContext.user ? (_jsx(Avatar, { variant: "circular", className: classes.avatar })) : (_jsx(UserAvatar, Object.assign({ hide: !scUserContext.user.community_badge }, { children: _jsx(Avatar, { alt: scUserContext.user.username, variant: "circular", src: scUserContext.user.avatar, classes: { root: classes.avatar } }) })))), secondary: _jsxs(Widget, Object.assign({ className: classNames(classes.comment, { [classes.hasValue]: !isEditorEmpty }) }, WidgetProps, { children: [media && media.length > 0 && _jsx(PreviewComponent, { value: media, onChange: handleChangeMedias, className: classes.media }), _jsx(Editor, Object.assign({ ref: editor, onChange: handleChangeText, defaultValue: html, editable: editable, uploadImage: true, action: replyIcon &&
|
|
135
|
+
onReply && (_jsx(IconButton, Object.assign({ onClick: handleReply, className: classes.iconReply, disabled: uploadingMedia }, { children: _jsx(Icon, { children: "send" }) }))) }, EditorProps, { MediaPluginProps: { isUploading: setUploadingMedia, onMediaAdd: handleChangeMedia } })), !isEditorEmpty && (_jsxs(Stack, Object.assign({ direction: "row", spacing: 2, className: classes.actions }, { children: [onReply && !replyIcon && (_jsx(LoadingButton, Object.assign({ variant: "outlined", size: "small", onClick: handleReply, loading: !editable, className: classes.buttonReply }, { children: _jsx(FormattedMessage, { id: "ui.commentObject.replyComment.reply", defaultMessage: "ui.commentObject.replyComment.reply" }) }))), onSave && (_jsxs(_Fragment, { children: [onCancel && (_jsx(LoadingButton, Object.assign({ variant: 'text', size: "small", onClick: handleCancel, disabled: !editable, color: "inherit", className: classes.buttonCancel }, { children: _jsx(FormattedMessage, { id: "ui.commentObject.replyComment.cancel", defaultMessage: "ui.commentObject.replyComment.cancel" }) }))), _jsx(LoadingButton, Object.assign({ variant: "outlined", size: "small", onClick: handleSave, loading: !editable, className: classes.buttonSave }, { children: _jsx(FormattedMessage, { id: "ui.commentObject.replyComment.save", defaultMessage: "ui.commentObject.replyComment.save" }) }))] }))] })))] })) })));
|
|
130
136
|
}
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback } from 'react';
|
|
3
|
-
import { Box, Typography } from '@mui/material';
|
|
2
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { Box, Icon, IconButton, InputAdornment, Typography } from '@mui/material';
|
|
4
4
|
import { styled } from '@mui/material/styles';
|
|
5
5
|
import classNames from 'classnames';
|
|
6
6
|
import Editor from '../../../Editor';
|
|
7
7
|
import { FormattedMessage } from 'react-intl';
|
|
8
8
|
import { PREFIX } from '../../constants';
|
|
9
|
+
import { File, Link } from '../../../../shared/Media';
|
|
10
|
+
import UrlTextField from '../../../../shared/Media/Link/UrlTextField';
|
|
9
11
|
const classes = {
|
|
10
12
|
root: `${PREFIX}-content-lesson-root`,
|
|
11
13
|
generalError: `${PREFIX}-general-error`,
|
|
12
14
|
title: `${PREFIX}-content-lesson-title`,
|
|
13
15
|
medias: `${PREFIX}-content-lesson-medias`,
|
|
14
|
-
editor: `${PREFIX}-content-lesson-editor
|
|
16
|
+
editor: `${PREFIX}-content-lesson-editor`,
|
|
17
|
+
link: `${PREFIX}-content-lesson-link`
|
|
15
18
|
};
|
|
16
19
|
const Root = styled(Box, {
|
|
17
20
|
name: PREFIX,
|
|
@@ -21,13 +24,41 @@ export default (props) => {
|
|
|
21
24
|
// PROPS
|
|
22
25
|
const { className = null, value, error = {}, disabled = false, onChange, onMediaChange, EditorProps = {} } = props;
|
|
23
26
|
const { error: generalError = null } = Object.assign({}, error);
|
|
27
|
+
const mediaObjectTypes = [File, Link];
|
|
28
|
+
const [medias, setMedias] = useState((value === null || value === void 0 ? void 0 : value.medias) || []);
|
|
29
|
+
const [openLink, setOpenLink] = useState();
|
|
30
|
+
const linkInputRef = useRef(null);
|
|
24
31
|
// HANDLERS
|
|
25
32
|
const handleChangeHtml = useCallback((html) => {
|
|
26
33
|
onChange(html);
|
|
27
34
|
}, [value]);
|
|
28
|
-
const
|
|
29
|
-
|
|
35
|
+
const handleChangeMedias = useCallback((value) => {
|
|
36
|
+
setMedias([...value]);
|
|
37
|
+
onMediaChange([...value]);
|
|
30
38
|
}, []);
|
|
39
|
+
const handleChangeMedia = (value) => {
|
|
40
|
+
setMedias((prev) => [...prev, value]);
|
|
41
|
+
onMediaChange([...medias, value]);
|
|
42
|
+
};
|
|
43
|
+
const handleLinkAdd = useCallback((media) => {
|
|
44
|
+
setMedias([...medias, media]);
|
|
45
|
+
setOpenLink(false);
|
|
46
|
+
}, [medias]);
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (openLink && linkInputRef.current) {
|
|
49
|
+
linkInputRef.current.scrollIntoView({ behavior: 'smooth' });
|
|
50
|
+
}
|
|
51
|
+
}, [openLink]);
|
|
31
52
|
// RENDER
|
|
32
|
-
return (_jsxs(Root, Object.assign({ className: classNames(classes.root, className) }, { children: [generalError && (_jsx(Typography, Object.assign({ className: classes.generalError }, { children: _jsx(FormattedMessage, { id: `ui.composer.error.${generalError}`, defaultMessage: `ui.composer.error.${generalError}` }) }))), _jsx(Editor, Object.assign({}, EditorProps, { editable: !disabled, className: classes.editor, onChange: handleChangeHtml, onMediaChange: handleChangeMedia, defaultValue: value.html
|
|
53
|
+
return (_jsxs(Root, Object.assign({ className: classNames(classes.root, className) }, { children: [generalError && (_jsx(Typography, Object.assign({ className: classes.generalError }, { children: _jsx(FormattedMessage, { id: `ui.composer.error.${generalError}`, defaultMessage: `ui.composer.error.${generalError}` }) }))), _jsx(Editor, Object.assign({}, EditorProps, { editable: !disabled, className: classes.editor, onChange: handleChangeHtml, onMediaChange: handleChangeMedia, defaultValue: value.html, ToolBarProps: {
|
|
54
|
+
customLink: _jsx(Link.triggerButton, { color: "default", onClick: () => setOpenLink(true) }, Link.name),
|
|
55
|
+
uploadImage: false,
|
|
56
|
+
uploadFile: true
|
|
57
|
+
} })), openLink && (_jsx(UrlTextField, { inputRef: linkInputRef, className: classes.link, id: "page", name: "page", label: _jsx(FormattedMessage, { id: "ui.composer.media.link.add.label", defaultMessage: "ui.composer.media.link.add.label" }), fullWidth: true, variant: "outlined", placeholder: "https://", onSuccess: handleLinkAdd, InputProps: {
|
|
58
|
+
endAdornment: (_jsx(InputAdornment, Object.assign({ position: "end" }, { children: _jsx(IconButton, Object.assign({ onClick: () => setOpenLink(false) }, { children: _jsx(Icon, { children: "close" }) })) })))
|
|
59
|
+
} })), medias && medias.length > 0 && (_jsx(Box, Object.assign({ className: classes.medias }, { children: mediaObjectTypes.map((mediaObjectType) => {
|
|
60
|
+
if (mediaObjectType.previewComponent) {
|
|
61
|
+
return _jsx(mediaObjectType.previewComponent, { value: medias, onChange: handleChangeMedias }, mediaObjectType.name);
|
|
62
|
+
}
|
|
63
|
+
}) })))] })));
|
|
33
64
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Avatar, Box, Button, Divider, Skeleton, Stack, Typography } from '@mui/material';
|
|
3
|
-
import { memo, useCallback, useEffect, useMemo, useReducer, useState } from 'react';
|
|
3
|
+
import { Fragment, memo, useCallback, useEffect, useMemo, useReducer, useState } from 'react';
|
|
4
4
|
import { CacheStrategies, Logger } from '@selfcommunity/utils';
|
|
5
5
|
import { SCOPE_SC_UI } from '../../../constants/Errors';
|
|
6
6
|
import { FormattedMessage } from 'react-intl';
|
|
@@ -94,11 +94,11 @@ function Comments(props) {
|
|
|
94
94
|
map.set(name, [...map.get(name), comment]);
|
|
95
95
|
}
|
|
96
96
|
});
|
|
97
|
-
return Array.from(map.entries()).map(([name, comments]) => (_jsxs(Box, Object.assign({ className: classes.outerWrapper }, { children: [_jsx(Typography, Object.assign({ variant: "h5" }, { children: name })), _jsx(Divider, {}), _jsxs(Stack, Object.assign({ className: classes.innerWrapper }, { children: [comments.map((comment) => (_jsxs(Stack, Object.assign({ className: classes.userWrapper }, { children: [_jsx(Avatar, { src: comment.created_by.avatar, alt: comment.created_by.username, className: classes.avatar }), _jsxs(Box, { children: [_jsxs(Stack, Object.assign({ className: classes.userInfo }, { children: [_jsx(Typography, Object.assign({ variant: "body1" }, { children: comment.created_by.username })), _jsx(Box, { className: classes.circle }), _jsx(Typography, Object.assign({ variant: "body2" }, { children: new Date(comment.created_at).toLocaleDateString() }))] })), _jsx(Typography,
|
|
97
|
+
return Array.from(map.entries()).map(([name, comments]) => (_jsxs(Box, Object.assign({ className: classes.outerWrapper }, { children: [_jsx(Typography, Object.assign({ variant: "h5" }, { children: name })), _jsx(Divider, {}), _jsxs(Stack, Object.assign({ className: classes.innerWrapper }, { children: [comments.map((comment) => (_jsxs(Stack, Object.assign({ className: classes.userWrapper }, { children: [_jsx(Avatar, { src: comment.created_by.avatar, alt: comment.created_by.username, className: classes.avatar }), _jsxs(Box, { children: [_jsxs(Stack, Object.assign({ className: classes.userInfo }, { children: [_jsx(Typography, Object.assign({ variant: "body1" }, { children: comment.created_by.username })), _jsx(Box, { className: classes.circle }), _jsx(Typography, Object.assign({ variant: "body2" }, { children: new Date(comment.created_at).toLocaleDateString() }))] })), _jsx(Typography, { variant: "body1", component: "div", dangerouslySetInnerHTML: { __html: comment.html } })] })] }), comment.id))), _jsx(Button, Object.assign({ component: Link, to: scRoutingContext.url(SCRoutes.COURSE_ROUTE_NAME, course), size: "small", variant: "outlined", color: "inherit", className: classes.button }, { children: _jsx(Typography, Object.assign({ variant: "body2" }, { children: _jsx(FormattedMessage, { id: "ui.course.dashboard.teacher.tab.comments.lessons.btn.label", defaultMessage: "ui.course.dashboard.teacher.tab.comments.lessons.btn.label" }) })) }))] }))] }), name)));
|
|
98
98
|
}, [state.results]);
|
|
99
99
|
if (!state.initialized) {
|
|
100
100
|
return _jsx(CommentsSkeleton, {});
|
|
101
101
|
}
|
|
102
|
-
return (
|
|
102
|
+
return (_jsx(Box, Object.assign({ className: classes.container }, { children: state.count > 0 ? (_jsxs(Fragment, { children: [_jsx(Typography, Object.assign({ variant: "body1" }, { children: _jsx(FormattedMessage, { id: "ui.course.dashboard.teacher.tab.comments.number", defaultMessage: "ui.course.dashboard.teacher.tab.comments.number", values: { commentsNumber: state.count } }) })), renderComments, isLoadingComments && _jsx(CommentSkeleton, { id: 1 }), _jsx(LoadingButton, Object.assign({ size: "small", variant: "outlined", color: "inherit", loading: isLoadingComments, disabled: !state.next, onClick: handleNext }, { children: _jsx(Typography, Object.assign({ variant: "body2" }, { children: _jsx(FormattedMessage, { id: "ui.course.dashboard.teacher.tab.comments.btn.label", defaultMessage: "ui.course.dashboard.teacher.tab.comments.btn.label" }) })) }))] })) : (_jsx(Typography, Object.assign({ variant: "body2" }, { children: _jsx(FormattedMessage, { id: "ui.course.dashboard.teacher.tab.comments.empty", defaultMessage: "ui.course.dashboard.teacher.tab.comments.empty" }) }))) })));
|
|
103
103
|
}
|
|
104
104
|
export default memo(Comments);
|
|
@@ -136,7 +136,7 @@ export default function CourseParticipantsButton(inProps) {
|
|
|
136
136
|
/**
|
|
137
137
|
* Rendering
|
|
138
138
|
*/
|
|
139
|
-
if (!participantsAvailable
|
|
139
|
+
if (!participantsAvailable) {
|
|
140
140
|
return _jsx(HiddenPlaceholder, {});
|
|
141
141
|
}
|
|
142
142
|
return (_jsxs(_Fragment, { children: [_jsxs(Root, Object.assign({ className: classNames(classes.root, className), onClick: handleToggleDialogOpen, disabled: loading || !scCourse || enrolled.length === 0,
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { MediaPluginProps } from './plugins';
|
|
3
|
+
import { ToolbarPluginProps } from './plugins/ToolbarPlugin';
|
|
2
4
|
import { SCMediaType } from '@selfcommunity/types';
|
|
3
5
|
export declare type EditorRef = {
|
|
4
6
|
focus: () => void;
|
|
@@ -48,7 +50,17 @@ export interface EditorProps {
|
|
|
48
50
|
* Handler for change media in the editor
|
|
49
51
|
* @default null
|
|
50
52
|
* */
|
|
51
|
-
onMediaChange?: (
|
|
53
|
+
onMediaChange?: (media: SCMediaType) => void;
|
|
54
|
+
/**
|
|
55
|
+
* Props to spread to ToolBar.
|
|
56
|
+
* @default {}
|
|
57
|
+
*/
|
|
58
|
+
ToolBarProps?: ToolbarPluginProps;
|
|
59
|
+
/**
|
|
60
|
+
* Props to spread to MediaPlugin.
|
|
61
|
+
* @default {}
|
|
62
|
+
*/
|
|
63
|
+
MediaPluginProps?: MediaPluginProps;
|
|
52
64
|
/**
|
|
53
65
|
* Handler for blur event of the editor
|
|
54
66
|
* @default null
|
|
@@ -67,8 +67,7 @@ const editorTheme = {
|
|
|
67
67
|
superscript: `${PREFIX}-textSuperscript`,
|
|
68
68
|
underline: `${PREFIX}-textUnderline`,
|
|
69
69
|
underlineStrikethrough: `${PREFIX}-textUnderlineStrikethrough`
|
|
70
|
-
}
|
|
71
|
-
document: `${PREFIX}-document`
|
|
70
|
+
}
|
|
72
71
|
};
|
|
73
72
|
/**
|
|
74
73
|
* > API documentation for the Community-JS Editor component. Learn about the available props and the CSS API.
|
|
@@ -105,7 +104,7 @@ const Editor = (inProps, ref) => {
|
|
|
105
104
|
props: inProps,
|
|
106
105
|
name: PREFIX
|
|
107
106
|
});
|
|
108
|
-
const { id = 'editor', className = null, defaultValue = '', toolbar = false, uploadImage = false, uploadFile = false, editable = true, onChange = null, onMediaChange = null, onFocus = null, onBlur = null, action = null, placeholder = _jsx(FormattedMessage, { id: "ui.editor.placeholder", defaultMessage: "ui.editor.placeholder" }) } = props;
|
|
107
|
+
const { id = 'editor', className = null, defaultValue = '', toolbar = false, uploadImage = false, uploadFile = false, editable = true, onChange = null, onMediaChange = null, onFocus = null, onBlur = null, action = null, ToolBarProps = {}, MediaPluginProps = {}, placeholder = _jsx(FormattedMessage, { id: "ui.editor.placeholder", defaultMessage: "ui.editor.placeholder" }) } = props;
|
|
109
108
|
const apiRef = useRef();
|
|
110
109
|
// STATE
|
|
111
110
|
const [focused, setFocused] = useState(false);
|
|
@@ -146,6 +145,6 @@ const Editor = (inProps, ref) => {
|
|
|
146
145
|
nodes: [...nodes],
|
|
147
146
|
theme: editorTheme
|
|
148
147
|
}), [editable]);
|
|
149
|
-
return (_jsx(Root, Object.assign({ id: id, className: classNames(classes.root, className, { [classes.toolbar]: toolbar, [classes.focused]: focused }) }, { children: _jsxs(LexicalComposer, Object.assign({ initialConfig: initialConfig }, { children: [toolbar ? (_jsxs(_Fragment, { children: [_jsx(ToolbarPlugin, { uploadImage: uploadImage, uploadFile: uploadFile, MediaPluginProps: { onMediaAdd: handleMediaChange } }), _jsx(ListPlugin, {}), _jsx(HorizontalRulePlugin, {})] })) : (_jsxs(Stack, Object.assign({ className: classes.actions, direction: "row" }, { children: [uploadImage && _jsx(ImagePlugin, {}), uploadFile && _jsx(MediaPlugin, {
|
|
148
|
+
return (_jsx(Root, Object.assign({ id: id, className: classNames(classes.root, className, { [classes.toolbar]: toolbar, [classes.focused]: focused }) }, { children: _jsxs(LexicalComposer, Object.assign({ initialConfig: initialConfig }, { children: [toolbar ? (_jsxs(_Fragment, { children: [_jsx(ToolbarPlugin, Object.assign({ uploadImage: uploadImage, uploadFile: uploadFile, MediaPluginProps: { onMediaAdd: handleMediaChange } }, ToolBarProps)), _jsx(ListPlugin, {}), _jsx(HorizontalRulePlugin, {})] })) : (_jsxs(Stack, Object.assign({ className: classes.actions, direction: "row" }, { children: [uploadImage && _jsx(ImagePlugin, {}), uploadFile && _jsx(MediaPlugin, Object.assign({}, MediaPluginProps)), _jsx(EmojiPlugin, {}), action && action] }))), _jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: classes.content }), placeholder: _jsx(Box, Object.assign({ className: classes.placeholder, onClick: handleFocus }, { children: placeholder })), ErrorBoundary: LexicalErrorBoundary }), _jsx(DefaultHtmlValuePlugin, { defaultValue: defaultValue }), _jsx(HistoryPlugin, {}), _jsx(OnChangePlugin, { onChange: handleChange }), _jsx(OnBlurPlugin, { onBlur: handleHasBlur }), _jsx(OnFocusPlugin, { onFocus: handleHasFocus }), _jsx(AutoLinkPlugin, {}), _jsx(MentionsPlugin, {}), _jsx(LinkPlugin, {}), _jsx(FloatingLinkPlugin, {}), _jsx(ApiPlugin, { ref: apiRef })] })) })));
|
|
150
149
|
};
|
|
151
150
|
export default forwardRef(Editor);
|
|
@@ -5,6 +5,5 @@ import { ImageNode } from './ImageNode';
|
|
|
5
5
|
import { MentionNode } from './MentionNode';
|
|
6
6
|
import { TextNode } from 'lexical';
|
|
7
7
|
import { HorizontalRuleNode } from '@lexical/react/LexicalHorizontalRuleNode';
|
|
8
|
-
|
|
9
|
-
declare const nodes: (typeof ImageNode | typeof TextNode | typeof MentionNode | typeof DocNode | typeof HorizontalRuleNode | typeof HeadingNode | typeof ListNode | typeof ListItemNode | typeof QuoteNode | typeof LinkNode)[];
|
|
8
|
+
declare const nodes: (typeof ImageNode | typeof TextNode | typeof MentionNode | typeof HorizontalRuleNode | typeof HeadingNode | typeof ListNode | typeof ListItemNode | typeof QuoteNode | typeof LinkNode)[];
|
|
10
9
|
export default nodes;
|
|
@@ -5,7 +5,6 @@ import { ImageNode } from './ImageNode';
|
|
|
5
5
|
import { MentionNode } from './MentionNode';
|
|
6
6
|
import { TextNode } from 'lexical';
|
|
7
7
|
import { HorizontalRuleNode } from '@lexical/react/LexicalHorizontalRuleNode';
|
|
8
|
-
import { DocNode } from './DocNode';
|
|
9
8
|
const nodes = [
|
|
10
9
|
HorizontalRuleNode,
|
|
11
10
|
HeadingNode,
|
|
@@ -17,7 +16,6 @@ const nodes = [
|
|
|
17
16
|
AutoLinkNode,
|
|
18
17
|
LinkNode,
|
|
19
18
|
ImageNode,
|
|
20
|
-
MentionNode
|
|
21
|
-
DocNode
|
|
19
|
+
MentionNode
|
|
22
20
|
];
|
|
23
21
|
export default nodes;
|
|
@@ -8,6 +8,7 @@ export interface InsertDocPayload {
|
|
|
8
8
|
export declare const INSERT_DOC_COMMAND: LexicalCommand<InsertDocPayload>;
|
|
9
9
|
export interface MediaPluginProps {
|
|
10
10
|
className?: string;
|
|
11
|
-
onMediaAdd?: (
|
|
11
|
+
onMediaAdd?: (media: SCMediaType) => void | null;
|
|
12
|
+
isUploading?: (boolean: any) => void;
|
|
12
13
|
}
|
|
13
14
|
export default function MediaPlugin(props: MediaPluginProps): JSX.Element;
|
|
@@ -14,7 +14,6 @@ import { asUploadButton } from '@rpldy/upload-button';
|
|
|
14
14
|
import { useSnackbar } from 'notistack';
|
|
15
15
|
import { $createImageNode, ImageNode } from '../nodes/ImageNode';
|
|
16
16
|
import { PREFIX } from '../constants';
|
|
17
|
-
import { $createDocNode, DocNode } from '../nodes/DocNode';
|
|
18
17
|
import { INSERT_IMAGE_COMMAND } from './ImagePlugin';
|
|
19
18
|
import classNames from 'classnames';
|
|
20
19
|
export const INSERT_DOC_COMMAND = createCommand();
|
|
@@ -31,11 +30,10 @@ const Root = styled(Box, {
|
|
|
31
30
|
})(() => ({}));
|
|
32
31
|
export default function MediaPlugin(props) {
|
|
33
32
|
var _a;
|
|
34
|
-
const { className = '', onMediaAdd = null } = props;
|
|
33
|
+
const { className = '', onMediaAdd = null, isUploading } = props;
|
|
35
34
|
// STATE
|
|
36
35
|
const [uploading, setUploading] = useState({});
|
|
37
36
|
const [mediaType, setMediaType] = useState('');
|
|
38
|
-
const [uploadedMedia, setUploadedMedia] = useState([]);
|
|
39
37
|
const [editor] = useLexicalComposerContext();
|
|
40
38
|
// CONTEXT
|
|
41
39
|
const scContext = useSCContext();
|
|
@@ -61,27 +59,6 @@ export default function MediaPlugin(props) {
|
|
|
61
59
|
return true;
|
|
62
60
|
}, COMMAND_PRIORITY_EDITOR);
|
|
63
61
|
}, [editor]);
|
|
64
|
-
useEffect(() => {
|
|
65
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
|
66
|
-
// @ts-ignore
|
|
67
|
-
if (!editor.hasNodes([DocNode])) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
editor.registerCommand(INSERT_DOC_COMMAND, (payload) => {
|
|
71
|
-
const docNode = $createDocNode({
|
|
72
|
-
src: payload.src,
|
|
73
|
-
name: payload.name,
|
|
74
|
-
type: payload.type
|
|
75
|
-
});
|
|
76
|
-
$insertNodeToNearestRoot(docNode);
|
|
77
|
-
return true;
|
|
78
|
-
}, COMMAND_PRIORITY_EDITOR);
|
|
79
|
-
}, [editor]);
|
|
80
|
-
useEffect(() => {
|
|
81
|
-
if (uploadedMedia.length > 0) {
|
|
82
|
-
onMediaAdd && onMediaAdd(uploadedMedia);
|
|
83
|
-
}
|
|
84
|
-
}, [uploadedMedia, onMediaAdd]);
|
|
85
62
|
// HANDLERS
|
|
86
63
|
const handleFileUploadFilter = (file) => {
|
|
87
64
|
if (file.type.startsWith('image/')) {
|
|
@@ -111,11 +88,12 @@ export default function MediaPlugin(props) {
|
|
|
111
88
|
};
|
|
112
89
|
editor.focus();
|
|
113
90
|
editor.dispatchCommand(INSERT_DOC_COMMAND, data);
|
|
114
|
-
|
|
91
|
+
onMediaAdd && onMediaAdd(media);
|
|
115
92
|
}
|
|
116
93
|
};
|
|
117
94
|
const handleUploadProgress = (chunks) => {
|
|
118
95
|
setUploading(Object.assign({}, chunks));
|
|
96
|
+
isUploading && isUploading(Object.keys(chunks).length !== 0);
|
|
119
97
|
};
|
|
120
98
|
const handleUploadError = (chunk, error) => {
|
|
121
99
|
enqueueSnackbar(error, {
|
|
@@ -125,14 +103,14 @@ export default function MediaPlugin(props) {
|
|
|
125
103
|
};
|
|
126
104
|
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
|
127
105
|
// @ts-ignore
|
|
128
|
-
if (!scUserContext.user || !editor.hasNodes([ImageNode
|
|
106
|
+
if (!scUserContext.user || !editor.hasNodes([ImageNode])) {
|
|
129
107
|
return null;
|
|
130
108
|
}
|
|
131
109
|
return (_jsx(Root, Object.assign({ className: classNames(classes.root, className) }, { children: _jsxs(ChunkedUploady, Object.assign({ destination: {
|
|
132
110
|
url: `${scContext.settings.portal}${Endpoints.ComposerChunkUploadMedia.url()}`,
|
|
133
111
|
headers: ((_a = scContext.settings.session) === null || _a === void 0 ? void 0 : _a.authToken) ? { Authorization: `Bearer ${scContext.settings.session.authToken.accessToken}` } : {},
|
|
134
112
|
method: Endpoints.ComposerChunkUploadMedia.method
|
|
135
|
-
}, chunkSize: 204800, accept: "image/*,application/*", fileFilter: handleFileUploadFilter }, { children: [_jsx(MediaChunkUploader, { type: mediaType, onSuccess: handleUploadSuccess, onProgress: handleUploadProgress, onError: handleUploadError }), _jsx(UploadButton, { className: className, extraProps: {
|
|
113
|
+
}, chunkSize: 204800, accept: "image/*,application/*", fileFilter: handleFileUploadFilter, multiple: true }, { children: [_jsx(MediaChunkUploader, { type: mediaType, onSuccess: handleUploadSuccess, onProgress: handleUploadProgress, onError: handleUploadError }), _jsx(UploadButton, { className: className, extraProps: {
|
|
136
114
|
disabled: Object.keys(uploading).length !== 0,
|
|
137
115
|
progress: Object.keys(uploading).length !== 0 ? Object.values(uploading)[0].completed : null
|
|
138
116
|
} })] })) })));
|
|
@@ -5,10 +5,12 @@
|
|
|
5
5
|
* LICENSE file in the root directory of this source tree.
|
|
6
6
|
*
|
|
7
7
|
*/
|
|
8
|
+
import * as React from 'react';
|
|
8
9
|
import { MediaPluginProps } from './MediaPlugin';
|
|
9
10
|
export interface ToolbarPluginProps {
|
|
10
11
|
uploadImage: boolean;
|
|
11
12
|
uploadFile?: boolean;
|
|
12
13
|
MediaPluginProps?: MediaPluginProps;
|
|
14
|
+
customLink?: React.ReactNode;
|
|
13
15
|
}
|
|
14
16
|
export default function ToolbarPlugin(inProps: ToolbarPluginProps): JSX.Element;
|
|
@@ -126,7 +126,7 @@ export default function ToolbarPlugin(inProps) {
|
|
|
126
126
|
props: inProps,
|
|
127
127
|
name: PREFIX
|
|
128
128
|
});
|
|
129
|
-
const { uploadImage = false, uploadFile = false, MediaPluginProps = {} } = props;
|
|
129
|
+
const { uploadImage = false, uploadFile = false, MediaPluginProps = {}, customLink = null } = props;
|
|
130
130
|
// STATE
|
|
131
131
|
const [editor] = useLexicalComposerContext();
|
|
132
132
|
const [activeEditor, setActiveEditor] = useState(editor);
|
|
@@ -269,5 +269,5 @@ export default function ToolbarPlugin(inProps) {
|
|
|
269
269
|
activeEditor.dispatchCommand(FORMAT_TEXT_COMMAND, format);
|
|
270
270
|
} }, { children: _jsx(Tooltip, Object.assign({ title: _jsx(FormattedMessage, { id: `ui.editor.toolbarPlugin.${format}`, defaultMessage: `ui.editor.toolbarPlugin.${format}` }) }, { children: _jsx(Icon, { children: `format_${format}` }) })) }), format))) })), _jsx(IconButton, Object.assign({ disabled: !isEditable, onClick: clearFormatting }, { children: _jsx(Tooltip, Object.assign({ title: _jsx(FormattedMessage, { id: "ui.editor.toolbarPlugin.clear", defaultMessage: "ui.editor.toolbarPlugin.clear" }) }, { children: _jsx(Icon, { children: "format_clear" }) })) })), _jsx(IconButton, Object.assign({ disabled: !isEditable, onClick: () => {
|
|
271
271
|
activeEditor.dispatchCommand(INSERT_HORIZONTAL_RULE_COMMAND, undefined);
|
|
272
|
-
} }, { children: _jsx(Tooltip, Object.assign({ title: _jsx(FormattedMessage, { id: "ui.editor.toolbarPlugin.horizontalRule", defaultMessage: "ui.editor.toolbarPlugin.horizontalRule" }) }, { children: _jsx(Icon, { children: "format_horizontal_rule" }) })) })), uploadImage && _jsx(ImagePlugin, {}), uploadFile && _jsx(MediaPlugin, Object.assign({}, MediaPluginProps)), _jsx(IconButton, Object.assign({ disabled: !isEditable, onClick: insertLink }, { children: _jsx(Tooltip, Object.assign({ title: _jsx(FormattedMessage, { id: "ui.editor.toolbarPlugin.link", defaultMessage: "ui.editor.toolbarPlugin.link" }) }, { children: _jsx(Icon, { children: "format_link" }) })) })), _jsx(EmojiPlugin, {})] })));
|
|
272
|
+
} }, { children: _jsx(Tooltip, Object.assign({ title: _jsx(FormattedMessage, { id: "ui.editor.toolbarPlugin.horizontalRule", defaultMessage: "ui.editor.toolbarPlugin.horizontalRule" }) }, { children: _jsx(Icon, { children: "format_horizontal_rule" }) })) })), uploadImage && _jsx(ImagePlugin, {}), uploadFile && _jsx(MediaPlugin, Object.assign({}, MediaPluginProps)), customLink !== null && customLink !== void 0 ? customLink : (_jsx(IconButton, Object.assign({ disabled: !isEditable, onClick: insertLink }, { children: _jsx(Tooltip, Object.assign({ title: _jsx(FormattedMessage, { id: "ui.editor.toolbarPlugin.link", defaultMessage: "ui.editor.toolbarPlugin.link" }) }, { children: _jsx(Icon, { children: "format_link" }) })) }))), _jsx(EmojiPlugin, {})] })));
|
|
273
273
|
}
|
|
@@ -85,6 +85,7 @@ export interface LessonCommentObjectProps {
|
|
|
85
85
|
|author|.SCCommentObject-author|Styles applied to the author section.|
|
|
86
86
|
|content|.SCCommentObject-content|Styles applied to content section.|
|
|
87
87
|
|textContent|.SCCommentObject-text-content|Styles applied to text content section.|
|
|
88
|
+
|mediaContent|.SCCommentObject-media-content|Styles applied to media content section.|
|
|
88
89
|
|commentActionsMenu|.SCCommentObject-comment-actions-menu|Styles applied to comment action menu element.|
|
|
89
90
|
|
|
90
91
|
|
|
@@ -22,6 +22,7 @@ import UserDeletedSnackBar from '../../shared/UserDeletedSnackBar';
|
|
|
22
22
|
import UserAvatar from '../../shared/UserAvatar';
|
|
23
23
|
import { PREFIX } from './constants';
|
|
24
24
|
import LessonCommentActionsMenu from '../../shared/LessonCommentActionsMenu';
|
|
25
|
+
import LessonFilePreview from '../../shared/LessonFilePreview';
|
|
25
26
|
const classes = {
|
|
26
27
|
root: `${PREFIX}-root`,
|
|
27
28
|
comment: `${PREFIX}-comment`,
|
|
@@ -29,6 +30,7 @@ const classes = {
|
|
|
29
30
|
content: `${PREFIX}-content`,
|
|
30
31
|
author: `${PREFIX}-author`,
|
|
31
32
|
textContent: `${PREFIX}-text-content`,
|
|
33
|
+
mediaContent: `${PREFIX}-media-content`,
|
|
32
34
|
commentActionsMenu: `${PREFIX}-comment-actions-menu`
|
|
33
35
|
};
|
|
34
36
|
const Root = styled(Box, {
|
|
@@ -63,6 +65,7 @@ const Root = styled(Box, {
|
|
|
63
65
|
|author|.SCCommentObject-author|Styles applied to the author section.|
|
|
64
66
|
|content|.SCCommentObject-content|Styles applied to content section.|
|
|
65
67
|
|textContent|.SCCommentObject-text-content|Styles applied to text content section.|
|
|
68
|
+
|mediaContent|.SCCommentObject-media-content|Styles applied to media content section.|
|
|
66
69
|
|commentActionsMenu|.SCCommentObject-comment-actions-menu|Styles applied to comment action menu element.|
|
|
67
70
|
|
|
68
71
|
|
|
@@ -152,7 +155,8 @@ export default function LessonCommentObject(inProps) {
|
|
|
152
155
|
const newObj = Object.assign({}, obj, {
|
|
153
156
|
text: data.text,
|
|
154
157
|
html: data.html,
|
|
155
|
-
created_at: data.created_at
|
|
158
|
+
created_at: data.created_at,
|
|
159
|
+
medias: medias
|
|
156
160
|
});
|
|
157
161
|
updateObject(newObj);
|
|
158
162
|
setEditComment(null);
|
|
@@ -179,7 +183,9 @@ export default function LessonCommentObject(inProps) {
|
|
|
179
183
|
return null;
|
|
180
184
|
}
|
|
181
185
|
const summaryHtml = getCommentContributionHtml(comment.html, scRoutingContext.url);
|
|
182
|
-
return (_jsx(React.Fragment, { children: editComment && editComment.id === comment.id ? (_jsx(Box, Object.assign({ className: classes.comment }, { children: _jsx(CommentObjectReply, Object.assign({ text: comment.html, medias: comment.medias, autoFocus: true, id: `edit-${comment.id}`, onSave: handleUpdate, onCancel: handleCancel, editable: !isSavingComment, EditorProps: { uploadFile: true, uploadImage: false } }, CommentObjectReplyProps)) }))) : (_jsx(BaseItem, { elevation: 0, className: classes.comment, image: _jsx(Link, Object.assign({}, (!comment.created_by.deleted && { to: scRoutingContext.url(SCRoutes.USER_PROFILE_ROUTE_NAME, comment.created_by) }), { onClick: comment.created_by.deleted ? () => setOpenAlert(true) : null }, { children: _jsx(UserAvatar, Object.assign({ hide: !obj.created_by.community_badge }, { children: _jsx(Avatar, { alt: obj.created_by.username, variant: "circular", src: comment.created_by.avatar, className: classes.avatar }) })) })), disableTypography: true, primary: _jsx(_Fragment, { children: _jsxs(Widget, Object.assign({ className: classes.content, elevation: elevation }, rest, { children: [_jsxs(CardContent, { children: [_jsx(Link, Object.assign({ className: classes.author }, (!comment.created_by.deleted && { to: scRoutingContext.url(SCRoutes.USER_PROFILE_ROUTE_NAME, comment.created_by) }), { onClick: comment.created_by.deleted ? () => setOpenAlert(true) : null }, { children: _jsx(Typography, Object.assign({ component: "span" }, { children: comment.created_by.username })) })), _jsxs(_Fragment, { children: [_jsx(Bullet, {}), _jsx(DateTimeAgo, { date: comment.created_at, showStartIcon: false })] }), _jsx(Typography, { className: classes.textContent, variant: "body2", gutterBottom: true, dangerouslySetInnerHTML: { __html: summaryHtml } })
|
|
186
|
+
return (_jsx(React.Fragment, { children: editComment && editComment.id === comment.id ? (_jsx(Box, Object.assign({ className: classes.comment }, { children: _jsx(CommentObjectReply, Object.assign({ text: comment.html, medias: comment.medias, autoFocus: true, id: `edit-${comment.id}`, onSave: handleUpdate, onCancel: handleCancel, editable: !isSavingComment, EditorProps: { uploadFile: true, uploadImage: false } }, CommentObjectReplyProps)) }))) : (_jsx(BaseItem, { elevation: 0, className: classes.comment, image: _jsx(Link, Object.assign({}, (!comment.created_by.deleted && { to: scRoutingContext.url(SCRoutes.USER_PROFILE_ROUTE_NAME, comment.created_by) }), { onClick: comment.created_by.deleted ? () => setOpenAlert(true) : null }, { children: _jsx(UserAvatar, Object.assign({ hide: !obj.created_by.community_badge }, { children: _jsx(Avatar, { alt: obj.created_by.username, variant: "circular", src: comment.created_by.avatar, className: classes.avatar }) })) })), disableTypography: true, primary: _jsx(_Fragment, { children: _jsxs(Widget, Object.assign({ className: classes.content, elevation: elevation }, rest, { children: [_jsxs(CardContent, { children: [_jsx(Link, Object.assign({ className: classes.author }, (!comment.created_by.deleted && { to: scRoutingContext.url(SCRoutes.USER_PROFILE_ROUTE_NAME, comment.created_by) }), { onClick: comment.created_by.deleted ? () => setOpenAlert(true) : null }, { children: _jsx(Typography, Object.assign({ component: "span" }, { children: comment.created_by.username })) })), _jsxs(_Fragment, { children: [_jsx(Bullet, {}), _jsx(DateTimeAgo, { date: comment.created_at, showStartIcon: false })] }), _jsx(Typography, { className: classes.textContent, variant: "body2", gutterBottom: true, dangerouslySetInnerHTML: { __html: summaryHtml } }), obj.medias && obj.medias.length > 0 && (_jsx(_Fragment, { children: obj.medias.map((media) => {
|
|
187
|
+
return _jsx(LessonFilePreview, { className: classes.mediaContent, media: media }, media.id);
|
|
188
|
+
}) }))] }), scUserContext.user && (_jsx(Box, Object.assign({ className: classes.commentActionsMenu }, { children: _jsx(LessonCommentActionsMenu, { lesson: lessonObject, commentObject: comment, onDelete: handleDelete, onEdit: handleEdit }) })))] })) }) })) }, comment.id));
|
|
183
189
|
}
|
|
184
190
|
/**
|
|
185
191
|
* Render comments
|
|
@@ -103,6 +103,11 @@ export default function LessonCommentObjects(inProps) {
|
|
|
103
103
|
var _a;
|
|
104
104
|
(_a = commentsEndRef.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ block: 'end', behavior: 'instant' });
|
|
105
105
|
};
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
if (commentsObject.comments.length > 0) {
|
|
108
|
+
scrollToBottom();
|
|
109
|
+
}
|
|
110
|
+
}, [commentsObject.comments]);
|
|
106
111
|
/**
|
|
107
112
|
* Perform save/update comment
|
|
108
113
|
*/
|
|
@@ -138,7 +143,6 @@ export default function LessonCommentObjects(inProps) {
|
|
|
138
143
|
handleCommentsUpdate(data);
|
|
139
144
|
setReplyKey(comment.id);
|
|
140
145
|
setIsCommenting(false);
|
|
141
|
-
scrollToBottom();
|
|
142
146
|
})
|
|
143
147
|
.catch((error) => {
|
|
144
148
|
Logger.error(SCOPE_SC_UI, error);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { __rest } from "tslib";
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useCallback } from 'react';
|
|
4
4
|
import { styled } from '@mui/material/styles';
|
|
5
5
|
import { useThemeProps } from '@mui/system';
|
|
@@ -12,12 +12,16 @@ import { getContributionHtml } from '../../utils/contribution';
|
|
|
12
12
|
import Widget from '../Widget';
|
|
13
13
|
import ContentLesson from '../Composer/Content/ContentLesson';
|
|
14
14
|
import HiddenPlaceholder from '../../shared/HiddenPlaceholder';
|
|
15
|
+
import DisplayComponent from '../../shared/Media/Link/DisplayComponent';
|
|
16
|
+
import LessonFilePreview from '../../shared/LessonFilePreview';
|
|
17
|
+
import { MediaTypes } from '@selfcommunity/api-services';
|
|
15
18
|
const classes = {
|
|
16
19
|
root: `${PREFIX}-root`,
|
|
17
20
|
content: `${PREFIX}-content`,
|
|
18
21
|
contentEdit: `${PREFIX}-content-edit`,
|
|
19
22
|
title: `${PREFIX}-title`,
|
|
20
23
|
text: `${PREFIX}-text`,
|
|
24
|
+
mediasSection: `${PREFIX}-medias-section`,
|
|
21
25
|
navigation: `${PREFIX}-navigation`,
|
|
22
26
|
editor: `${PREFIX}-editor`
|
|
23
27
|
};
|
|
@@ -52,7 +56,7 @@ export default function LessonObject(inProps) {
|
|
|
52
56
|
}
|
|
53
57
|
return (_jsx(Root, Object.assign({ className: classNames(className, classes.root) }, rest, { children: _jsx(Widget, { children: _jsx(CardContent, Object.assign({ classes: { root: editMode ? classes.contentEdit : classes.content } }, { children: editMode ? (_jsx(ContentLesson, { value: lesson,
|
|
54
58
|
//error={{error}}
|
|
55
|
-
onChange: handleChangeLesson, onMediaChange: handleChangeMedia, disabled: isSubmitting, EditorProps: Object.assign({ toolbar: true
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
onChange: handleChangeLesson, onMediaChange: handleChangeMedia, disabled: isSubmitting, EditorProps: Object.assign({ toolbar: true }, EditorProps) })) : (_jsxs(_Fragment, { children: [_jsx(Typography, { component: "div", gutterBottom: true, className: classes.text, dangerouslySetInnerHTML: {
|
|
60
|
+
__html: getContributionHtml(lesson.html, scRoutingContext.url)
|
|
61
|
+
} }), lesson.medias && lesson.medias.length > 0 && (_jsx(Box, Object.assign({ className: classes.mediasSection }, { children: lesson.medias.map((media) => media.type === MediaTypes.URL ? (_jsx(DisplayComponent, { medias: [media] }, media.id)) : (_jsx(LessonFilePreview, { media: media }, media.id))) })))] })) })) }) })));
|
|
58
62
|
}
|
|
@@ -2,12 +2,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Accordion, AccordionDetails, AccordionSummary, Box, Icon, styled, Typography, useMediaQuery, useTheme, useThemeProps } from '@mui/material';
|
|
3
3
|
import { FormattedMessage } from 'react-intl';
|
|
4
4
|
import classNames from 'classnames';
|
|
5
|
-
import {
|
|
5
|
+
import { useCallback, useState } from 'react';
|
|
6
6
|
import { SCCourseLessonCompletionStatusType } from '@selfcommunity/types';
|
|
7
7
|
import { PREFIX } from './constants';
|
|
8
8
|
import AccordionLessonSkeleton from './Skeleton';
|
|
9
9
|
const classes = {
|
|
10
|
-
root: `${PREFIX}-
|
|
10
|
+
root: `${PREFIX}-root`,
|
|
11
|
+
empty: `${PREFIX}-empty`,
|
|
11
12
|
accordion: `${PREFIX}-accordion`,
|
|
12
13
|
summary: `${PREFIX}-summary`,
|
|
13
14
|
details: `${PREFIX}-details`,
|
|
@@ -38,7 +39,7 @@ export default function AccordionLessons(inProps) {
|
|
|
38
39
|
if (!course) {
|
|
39
40
|
return _jsx(AccordionLessonSkeleton, {});
|
|
40
41
|
}
|
|
41
|
-
return (_jsx(
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
return (_jsx(Root, Object.assign({ className: classNames(classes.root, className) }, { children: ((_a = course.sections) === null || _a === void 0 ? void 0 : _a.length) > 0 ? (course.sections.map((section) => (_jsxs(Accordion, Object.assign({ className: classes.accordion, expanded: expanded === section.id, onChange: handleChange(section.id), disableGutters: true, elevation: 0, square: true }, { children: [_jsxs(AccordionSummary, Object.assign({ className: classes.summary, expandIcon: _jsx(Icon, { children: "expand_less" }) }, { children: [_jsx(Typography, Object.assign({ component: "span", variant: "body1" }, { children: section.name })), !isMobile && (_jsx(Typography, Object.assign({ component: "span", variant: "body1" }, { children: _jsx(FormattedMessage, { id: "ui.course.table.lessons.title", defaultMessage: "ui.course.table.lessons.title", values: {
|
|
43
|
+
lessonsNumber: section.lessons.length
|
|
44
|
+
} }) })))] })), section.lessons.map((lesson) => (_jsxs(AccordionDetails, Object.assign({ className: classes.details }, { children: [lesson.completion_status === SCCourseLessonCompletionStatusType.COMPLETED ? (_jsx(Icon, Object.assign({ fontSize: "small", color: "primary" }, { children: "circle_checked" }))) : lesson.locked ? (_jsx(Icon, { children: "private" })) : (_jsx(Box, { className: classes.circle })), _jsx(Typography, { children: lesson.name })] }), lesson.id)))] }), section.id)))) : (_jsx(Typography, Object.assign({ variant: "body1", className: classes.empty }, { children: _jsx(FormattedMessage, { id: "ui.course.accordionLessons.empty", defaultMessage: "ui.course.accordionLessons.empty" }) }))) })));
|
|
44
45
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface LessonFilePreviewProps {
|
|
2
|
+
/**
|
|
3
|
+
* Overrides or extends the styles applied to the component.
|
|
4
|
+
* @default null
|
|
5
|
+
*/
|
|
6
|
+
className?: string;
|
|
7
|
+
/**
|
|
8
|
+
* The media object to show
|
|
9
|
+
*/
|
|
10
|
+
media: any;
|
|
11
|
+
}
|
|
12
|
+
export default function LessonFilePreview(props: LessonFilePreviewProps): JSX.Element;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { styled } from '@mui/material/styles';
|
|
3
|
+
import classNames from 'classnames';
|
|
4
|
+
import { Box, Link, Typography } from '@mui/material';
|
|
5
|
+
import Icon from '@mui/material/Icon';
|
|
6
|
+
import { MEDIA_TYPE_DOCUMENT } from '../../constants/Media';
|
|
7
|
+
const PREFIX = 'SCLessonFilePreview';
|
|
8
|
+
const classes = {
|
|
9
|
+
root: `${PREFIX}-root`,
|
|
10
|
+
item: `${PREFIX}-item`,
|
|
11
|
+
title: `${PREFIX}-title`
|
|
12
|
+
};
|
|
13
|
+
const Root = styled(Box, {
|
|
14
|
+
name: PREFIX,
|
|
15
|
+
slot: 'Root',
|
|
16
|
+
overridesResolver: (props, styles) => styles.root
|
|
17
|
+
})(() => ({}));
|
|
18
|
+
export default function LessonFilePreview(props) {
|
|
19
|
+
// PROPS
|
|
20
|
+
const { className, media } = props;
|
|
21
|
+
/**
|
|
22
|
+
* Renders component
|
|
23
|
+
*/
|
|
24
|
+
return (_jsx(Root, Object.assign({ className: classNames(classes.root, className), sx: { backgroundImage: `url(${(media === null || media === void 0 ? void 0 : media.image_thumbnail) ? media.image_thumbnail.url : media.image})` } }, { children: media.title && (_jsx(Link, Object.assign({ href: media.url, target: "_blank", rel: "noopener noreferrer" }, { children: _jsxs(Typography, Object.assign({ className: classes.title }, { children: [media.type === MEDIA_TYPE_DOCUMENT && _jsx(Icon, { children: "picture_as_pdf" }), media.title] })) }))) }), media.id));
|
|
25
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { __rest } from "tslib";
|
|
2
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import React, { useEffect, useState } from 'react';
|
|
4
4
|
import InputAdornment from '@mui/material/InputAdornment';
|
|
5
5
|
import IconButton from '@mui/material/IconButton';
|
|
@@ -14,6 +14,7 @@ const INITIAL_STATE = {
|
|
|
14
14
|
urlError: null
|
|
15
15
|
};
|
|
16
16
|
export default (props) => {
|
|
17
|
+
var _a, _b;
|
|
17
18
|
// STATE
|
|
18
19
|
const [isCreating, setIsCreating] = useState(false);
|
|
19
20
|
const [state, setState] = useState(Object.assign({}, INITIAL_STATE));
|
|
@@ -73,7 +74,5 @@ export default (props) => {
|
|
|
73
74
|
* Renders url text field
|
|
74
75
|
*/
|
|
75
76
|
return (_jsx("form", Object.assign({ method: "post", onSubmit: handleSubmit }, { children: _jsx(TextField, Object.assign({ value: url, type: "url", onChange: handleChange, onPaste: handlePaste, error: error || Boolean(urlError), helperText: helperText ||
|
|
76
|
-
(urlError && (_jsx(FormattedMessage, { id: `ui.composer.media.link.add.error.${urlError}`, defaultMessage: `ui.composer.media.link.add.error.${urlError}` }))) || _jsx(FormattedMessage, { id: "ui.composer.media.link.add.help", defaultMessage: "ui.composer.media.link.add.help" }), disabled: isCreating }, rest, { InputProps: {
|
|
77
|
-
endAdornment: (_jsx(InputAdornment, Object.assign({ position: "end" }, { children: _jsx(Fade, Object.assign({ in: urlError === null && url !== '' }, { children: _jsx(IconButton, Object.assign({ size: "small", disabled: isCreating, type: "submit" }, { children: isCreating ? _jsx(CircularProgress, { color: "primary", size: 20 }) : _jsx(FormattedMessage, { id: "ui.composer.media.link.add.submit", defaultMessage: "ui.composer.media.link.add.submit" }) })) })) })))
|
|
78
|
-
} })) })));
|
|
77
|
+
(urlError && (_jsx(FormattedMessage, { id: `ui.composer.media.link.add.error.${urlError}`, defaultMessage: `ui.composer.media.link.add.error.${urlError}` }))) || _jsx(FormattedMessage, { id: "ui.composer.media.link.add.help", defaultMessage: "ui.composer.media.link.add.help" }), disabled: isCreating }, rest, { InputProps: Object.assign(Object.assign({}, rest.InputProps), { endAdornment: (_jsx(_Fragment, { children: url === '' && ((_a = rest.InputProps) === null || _a === void 0 ? void 0 : _a.endAdornment) ? ((_b = rest.InputProps) === null || _b === void 0 ? void 0 : _b.endAdornment) : (_jsx(InputAdornment, Object.assign({ position: "end" }, { children: _jsx(Fade, Object.assign({ in: urlError === null && url !== '' }, { children: _jsx(IconButton, Object.assign({ size: "small", disabled: isCreating, type: "submit" }, { children: isCreating ? (_jsx(CircularProgress, { color: "primary", size: 20 })) : (_jsx(FormattedMessage, { id: "ui.composer.media.link.add.submit", defaultMessage: "ui.composer.media.link.add.submit" })) })) })) }))) })) }) })) })));
|
|
79
78
|
};
|