Rolled the reply field into CommentWidget, where it belongs
This commit is contained in:
parent
dcef9d5cab
commit
0e70226aaa
|
@ -1,7 +1,6 @@
|
|||
import React, { useState, useRef, useEffect } from 'react';
|
||||
import setPosition from './setPosition';
|
||||
import TagWidget from './widgets/tag/TagWidget';
|
||||
import TypeSelectorWidget from './widgets/type/TypeSelectorWidget';
|
||||
import CommentWidget from './widgets/comment/CommentWidget';
|
||||
|
||||
/**
|
||||
|
@ -15,7 +14,6 @@ const Editor = props => {
|
|||
|
||||
// The current state of the edited annotation vs. original
|
||||
const [ currentAnnotation, setCurrentAnnotation ] = useState();
|
||||
const [ currentReply, setCurrentReply ] = useState('');
|
||||
|
||||
// Reference to the DOM element, so we can set position
|
||||
const element = useRef();
|
||||
|
@ -23,7 +21,6 @@ const Editor = props => {
|
|||
// Re-render: set derived annotation state & position the editor
|
||||
useEffect(() => {
|
||||
setCurrentAnnotation(props.annotation);
|
||||
setCurrentReply('');
|
||||
|
||||
if (element.current)
|
||||
setPosition(props.containerEl, element.current, props.bounds);
|
||||
|
@ -48,25 +45,24 @@ const Editor = props => {
|
|||
);
|
||||
|
||||
const onOk = _ => {
|
||||
// If there is a non-empty reply, append it as a comment body
|
||||
const updated = currentReply.trim() ?
|
||||
currentAnnotation.clone({
|
||||
body: [ ...currentAnnotation.bodies, { type: 'TextualBody', value: currentReply.trim() } ]
|
||||
}) : currentAnnotation;
|
||||
// Removes the 'draft' flag from all bodies
|
||||
const undraft = annotation => annotation.clone({
|
||||
body : annotation.bodies.map(({ draft, ...rest }) => rest)
|
||||
});
|
||||
|
||||
// Current annotation is either a selection (if it was created from
|
||||
// scratch just now) or an annotation (if it existed already and was
|
||||
// opened for editing)
|
||||
if (updated.bodies.length === 0) {
|
||||
if (updated.isSelection)
|
||||
if (currentAnnotation.bodies.length === 0) {
|
||||
if (currentAnnotation.isSelection)
|
||||
props.onCancel();
|
||||
else
|
||||
props.onAnnotationDeleted(props.annotation);
|
||||
} else {
|
||||
if (updated.isSelection)
|
||||
props.onAnnotationCreated(updated.toAnnotation());
|
||||
if (currentAnnotation.isSelection)
|
||||
props.onAnnotationCreated(undraft(currentAnnotation).toAnnotation());
|
||||
else
|
||||
props.onAnnotationUpdated(updated, props.annotation);
|
||||
props.onAnnotationUpdated(undraft(currentAnnotation), props.annotation);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -76,16 +72,15 @@ const Editor = props => {
|
|||
<div className="inner">
|
||||
<CommentWidget
|
||||
annotation={currentAnnotation}
|
||||
currentReply={currentReply}
|
||||
onUpdateComment={onUpdateBody}
|
||||
onDeleteComment={onRemoveBody}
|
||||
onUpdateReply={evt => setCurrentReply(evt.target.value.trim())}
|
||||
onOk={onOk} />
|
||||
onAppendBody={onAppendBody}
|
||||
onUpdateBody={onUpdateBody}
|
||||
onRemoveBody={onRemoveBody}
|
||||
onSaveAndClose={onOk} />
|
||||
|
||||
<TagWidget
|
||||
annotation={currentAnnotation}
|
||||
onAddTag={onAppendBody}
|
||||
onRemoveTag={onRemoveBody} />
|
||||
onAppendBody={onAppendBody}
|
||||
onRemoveBody={onRemoveBody} />
|
||||
|
||||
{ props.readOnly ? (
|
||||
<div className="footer">
|
||||
|
|
|
@ -2,10 +2,8 @@ import Editor from './Editor';
|
|||
|
||||
import CommentWidget from './widgets/comment/CommentWidget';
|
||||
import TagWidget from './widgets/tag/TagWidget';
|
||||
import TypeSelectorWidget from './widgets/type/TypeSelectorWidget';
|
||||
|
||||
Editor.CommentWidget = CommentWidget;
|
||||
Editor.TagWidget = TagWidget;
|
||||
Editor.TypeSelectorWidget = TypeSelectorWidget;
|
||||
|
||||
export { Editor };
|
||||
|
|
|
@ -32,7 +32,7 @@ const Comment = props => {
|
|||
editable={isEditable}
|
||||
content={props.body.value}
|
||||
onChange={onUpdateComment}
|
||||
onOk={props.onOk}
|
||||
onSaveAndClose={props.onSaveAndClose}
|
||||
/>
|
||||
|
||||
<div
|
||||
|
|
|
@ -2,36 +2,72 @@ import React from 'react';
|
|||
import Comment from './Comment';
|
||||
import TextEntryField from './TextEntryField';
|
||||
|
||||
/**
|
||||
* Comments are TextualBodies where the purpose field is either
|
||||
* blank or 'commenting' or 'replying'
|
||||
*/
|
||||
const isComment = body =>
|
||||
body.type === 'TextualBody' && (
|
||||
!body.hasOwnProperty('purpose') || body.purpose === 'commenting' || body.purpose === 'replying'
|
||||
);
|
||||
|
||||
/**
|
||||
* The draft reply is a comment body with a 'draft' flag
|
||||
*/
|
||||
const getDraftReply = (existingDraft, isReply) => {
|
||||
return existingDraft ? existingDraft : {
|
||||
type: 'TextualBody', value: '', purpose: isReply ? 'replying' : 'commenting', draft: true
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders a list of comment bodies, followed by a 'reply' field.
|
||||
*/
|
||||
const CommentWidget = props => {
|
||||
|
||||
const commentBodies = props.annotation ?
|
||||
props.annotation.bodies.filter(b => // No purpose or 'commenting', 'replying'
|
||||
!b.hasOwnProperty('purpose') || b.purpose === 'commenting' || b.purpose === 'replying'
|
||||
) : [];
|
||||
// All comments (draft + non-draft)
|
||||
const all = props.annotation ?
|
||||
props.annotation.bodies.filter(isComment) : [];
|
||||
|
||||
// Non-draft comments
|
||||
const comments = all.filter(b => !b.draft);
|
||||
|
||||
// Draft reply
|
||||
const draftReply = getDraftReply(all.find(b => b.draft), comments.length > 0);
|
||||
|
||||
const onEditReply = evt => {
|
||||
const prev = draftReply.value.trim();
|
||||
const updated = evt.target.value.trim();
|
||||
|
||||
if (prev.length === 0 && updated.length > 0) {
|
||||
props.onAppendBody({ ...draftReply, value: updated });
|
||||
} else if (prev.length > 0 && updated.length === 0) {
|
||||
props.onRemoveBody(draftReply);
|
||||
} else {
|
||||
props.onUpdateBody(draftReply, { ...draftReply, value: updated });
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{ commentBodies.map((body, idx) =>
|
||||
{ comments.map((body, idx) =>
|
||||
<Comment
|
||||
key={idx}
|
||||
readOnly={props.readOnly}
|
||||
body={body}
|
||||
onUpdate={props.onUpdateComment}
|
||||
onDelete={props.onDeleteComment}
|
||||
onOk={props.onOk} />
|
||||
onUpdate={props.onUpdateBody}
|
||||
onDelete={props.onRemoveBody}
|
||||
onSaveAndClose={props.onSaveAndClose} />
|
||||
)}
|
||||
|
||||
{ !props.readOnly && props.annotation &&
|
||||
<div className="r6o-widget comment editable">
|
||||
<TextEntryField
|
||||
content={props.currentReply}
|
||||
content={draftReply.value}
|
||||
editable={true}
|
||||
placeholder={commentBodies.length > 0 ? 'Add a reply...' : 'Add a comment...'}
|
||||
onChange={props.onUpdateReply}
|
||||
onOk={() => props.onOk()}
|
||||
placeholder={comments.length > 0 ? 'Add a reply...' : 'Add a comment...'}
|
||||
onChange={onEditReply}
|
||||
onSaveAndClose={() => props.onSaveAndClose()}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ export default class TextEntryField extends Component {
|
|||
// CTRL+Enter functions as Ok
|
||||
onKeyDown = evt => {
|
||||
if (evt.which === 13 && evt.ctrlKey)
|
||||
this.props.onOk();
|
||||
this.props.onSaveAndClose();
|
||||
}
|
||||
|
||||
// Focus on render
|
||||
|
|
Loading…
Reference in New Issue