Skip to content

Commit 2ea48a0

Browse files
committed
Merge branch '4xdk-editable-descriptions'
2 parents 60e671a + dd5c26f commit 2ea48a0

5 files changed

Lines changed: 173 additions & 14 deletions

File tree

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@
33
"version": "0.1.0",
44
"private": true,
55
"dependencies": {
6+
"draft-js": "^0.10.5",
7+
"draft-js-autolist-plugin": "^2.0.0",
8+
"draft-js-markdown-plugin": "^3.0.0",
9+
"draft-js-plugins-editor": "^2.0.8",
610
"glamor": "^2.20.40",
11+
"markdown-draft-js": "^1.0.1",
12+
"prismjs": "^1.15.0",
713
"react": "^15.6.1",
814
"react-animated-number": "^0.4.3",
915
"react-big-calendar": "^0.19.0",

src/components/Content/RightPanel/TaskDetails/TaskDetails.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@
115115

116116
.subtasks-list {
117117
list-style: none;
118-
padding-left: 0
118+
padding-left: 0;
119119
}
120120

121121
.subtask-item {

src/components/Content/RightPanel/TaskDetails/TaskDetails.js

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ import { connect } from 'react-redux';
33
import { withRouter } from 'react-router-dom';
44
import { showTaskInPursuance } from '../../../../utils/tasks';
55
import { getPursuances, getTasks, getUsers, rpShowTaskDetails, patchTask } from '../../../../actions';
6-
import ReactMarkdown from 'react-markdown';
76
import FaCircleO from 'react-icons/lib/fa/circle-o';
87
import TaskDetailsTopbar from './TaskDetailsTopbar';
98
import TaskTitle from './TaskTitle/TaskTitle';
109
import TaskIcons from './TaskIcons/TaskIcons';
1110
import TaskForm from '../../TaskManager/TaskForm/TaskForm';
11+
import Wysiwyg from './Wysiwyg/Wysiwyg';
1212

1313
import './TaskDetails.css';
1414

@@ -92,18 +92,7 @@ class TaskDetails extends Component {
9292
</span>
9393
</div>
9494
<div className="task-deliverables-ctn">
95-
<h4><strong>Description / Deliverables</strong></h4>
96-
<span>
97-
<ReactMarkdown
98-
source={task.deliverables}
99-
render={{Link: props => {
100-
if (props.href.startsWith('/')) {
101-
return <a href={props.href}>{props.children}</a>;
102-
}
103-
// If link to external site, open in new tab
104-
return <a href={props.href} target="_blank">{props.children}</a>;
105-
}}} />
106-
</span>
95+
<Wysiwyg taskGid={taskGid} attributeName='deliverables' patchTask={this.props.patchTask} />
10796
</div>
10897
<div className="subtasks-ctn">
10998
<h4><strong>Subtasks</strong></h4>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
.wysiwyg {
2+
box-sizing: border-box;
3+
border: 1px solid #ccc;
4+
cursor: text;
5+
padding: 8px;
6+
border-radius: 5px;
7+
margin: 10px 0;
8+
background: #fefefe;
9+
color: black;
10+
border-bottom: 2px solid #50b3fe;
11+
}
12+
13+
.wysiwyg :global(.public-DraftEditor-content) {
14+
min-height: 140px;
15+
}
16+
17+
.wysiwyg-save,
18+
.wysiwyg-edit {
19+
border-radius: 5px;
20+
background-color: #000;
21+
height: 28px;
22+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import React, {Component} from 'react';
2+
import {connect} from 'react-redux';
3+
import {EditorState, convertToRaw, convertFromRaw} from 'draft-js';
4+
import { Editor } from 'draft-js';
5+
import {getDefaultKeyBinding, KeyBindingUtil} from 'draft-js';
6+
import createAutoListPlugin from 'draft-js-autolist-plugin'
7+
import createMarkdownPlugin from 'draft-js-markdown-plugin';
8+
import {markdownToDraft, draftToMarkdown} from 'markdown-draft-js';
9+
import ReactMarkdown from 'react-markdown';
10+
import './Wysiwyg.css';
11+
12+
const {hasCommandModifier} = KeyBindingUtil;
13+
14+
function myKeyBindingFn(e) {
15+
if (e.keyCode === 13 /* `enter` key */ && hasCommandModifier(e)) {
16+
return 'myeditor-save';
17+
}
18+
return getDefaultKeyBinding(e);
19+
}
20+
21+
const autoListPlugin = createAutoListPlugin();
22+
23+
const plugins = [
24+
autoListPlugin,
25+
createMarkdownPlugin()
26+
];
27+
28+
class Wysiwyg extends Component {
29+
constructor(props) {
30+
super(props);
31+
const content = this.calculateContent();
32+
33+
this.state = {
34+
editMode: false,
35+
editorState: EditorState.createWithContent(convertFromRaw(content))
36+
};
37+
}
38+
39+
calculateContent = () => {
40+
const {tasks: { taskMap }, taskGid, attributeName} = this.props,
41+
attributeValue = taskMap[taskGid][attributeName],
42+
content = attributeValue ? markdownToDraft(attributeValue, {
43+
remarkableOptions: {
44+
html: false,
45+
preserveNewlines: true
46+
}
47+
}) : markdownToDraft('');
48+
49+
return content;
50+
}
51+
52+
editModeEnable = () => {
53+
const content = this.calculateContent();
54+
55+
this.setState({
56+
editMode: true,
57+
editorState: EditorState.moveFocusToEnd(
58+
EditorState.createWithContent(convertFromRaw(content))
59+
)
60+
});
61+
};
62+
63+
onChange = (editorState) => {
64+
this.setState({
65+
editorState
66+
});
67+
};
68+
69+
save = () => {
70+
const {patchTask} = this.props,
71+
markdown = draftToMarkdown(convertToRaw(this.state.editorState.getCurrentContent())),
72+
payload = {gid: this.props.taskGid};
73+
74+
payload[this.props.attributeName] = markdown;
75+
76+
patchTask(payload);
77+
78+
this.setState({
79+
editMode: false
80+
});
81+
}
82+
83+
editModeFalse = () => {
84+
this.setState({
85+
editMode: false
86+
});
87+
}
88+
89+
handleKeyCommand = (command) => {
90+
if (command === 'myeditor-save') {
91+
this.save();
92+
return 'handled';
93+
}
94+
return 'not-handled';
95+
}
96+
97+
render() {
98+
const {tasks: {taskMap}} = this.props,
99+
attributeValue = taskMap[this.props.taskGid][this.props.attributeName];
100+
101+
return (
102+
<div>
103+
<h4>
104+
<strong>Description / Deliverables</strong>&nbsp;&nbsp;
105+
{this.state.editMode && <button className='wysiwyg-save' onClick={this.save}>Save</button>}
106+
{!this.state.editMode && <button className='wysiwyg-edit' onClick={this.editModeEnable}>Edit</button>}
107+
</h4>
108+
{
109+
this.state.editMode && (
110+
<div className='wysiwyg'>
111+
<Editor
112+
editorState={this.state.editorState}
113+
onChange={this.onChange}
114+
onEscape={this.editModeFalse}
115+
plugins={plugins}
116+
handleKeyCommand={this.handleKeyCommand}
117+
keyBindingFn={myKeyBindingFn}
118+
/>
119+
</div>
120+
)
121+
}
122+
{
123+
!this.state.editMode && (
124+
<div>
125+
<ReactMarkdown
126+
source={attributeValue}
127+
render={{Link: props => {
128+
if (props.href.startsWith('/')) {
129+
return <a href={props.href}>{props.children}</a>;
130+
}
131+
// If link to external site, open in new tab
132+
return <a href={props.href} target="_blank">{props.children}</a>;
133+
}}} />
134+
</div>
135+
)
136+
}
137+
</div>
138+
);
139+
}
140+
}
141+
142+
export default connect(({tasks}) => ({tasks}), null)(Wysiwyg);

0 commit comments

Comments
 (0)