Skip to content

Commit 816efff

Browse files
committed
Adding snippets for smarter copy-pasting (parts of) review comments.
1 parent 6cdd87f commit 816efff

File tree

11 files changed

+646
-229
lines changed

11 files changed

+646
-229
lines changed

src/components/Solutions/SolutionStatus/SolutionStatus.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ class SolutionStatus extends Component {
211211
note
212212
) : (
213213
<em className="text-body-secondary small">
214-
<FormattedMessage id="app.solution.emptyNote" defaultMessage="empty" />
214+
<FormattedMessage id="generic.empty" defaultMessage="empty" />
215215
</em>
216216
)}
217217

src/components/forms/Fields/MarkdownTextAreaField.js

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -25,45 +25,50 @@ class MarkdownTextAreaField extends Component {
2525
const {
2626
input: { name, value },
2727
disabled,
28+
hideMarkdownPreview = false,
2829
} = this.props;
2930
const { showPreview } = this.state;
3031
return (
3132
<div>
3233
<SourceCodeField {...this.props} mode="markdown" readOnly={disabled} />
33-
<Row>
34-
<Col sm={4}>
35-
<OnOffCheckbox name={`${name}.togglePreview`} checked={showPreview} onChange={this.toggleShowPreview}>
36-
<FormattedMessage id="app.markdownTextArea.showPreview" defaultMessage="Preview" />
37-
</OnOffCheckbox>
38-
</Col>
39-
<Col sm={8} className="text-end">
40-
<Form.Text>
41-
<FormattedMessage
42-
id="app.markdownTextArea.canUseMarkdown"
43-
defaultMessage="You can use <a>markdown syntax</a> in this field."
44-
values={{
45-
a: caption => (
46-
<a href="https://github.com/ReCodEx/wiki/wiki/Markdown" target="_blank" rel="noreferrer">
47-
{caption}
48-
</a>
49-
),
50-
}}
51-
/>
52-
</Form.Text>
53-
</Col>
54-
</Row>
34+
{!hideMarkdownPreview && (
35+
<>
36+
<Row>
37+
<Col sm={4}>
38+
<OnOffCheckbox name={`${name}.togglePreview`} checked={showPreview} onChange={this.toggleShowPreview}>
39+
<FormattedMessage id="app.markdownTextArea.showPreview" defaultMessage="Preview" />
40+
</OnOffCheckbox>
41+
</Col>
42+
<Col sm={8} className="text-end">
43+
<Form.Text>
44+
<FormattedMessage
45+
id="app.markdownTextArea.canUseMarkdown"
46+
defaultMessage="You can use <a>markdown syntax</a> in this field."
47+
values={{
48+
a: caption => (
49+
<a href="https://github.com/ReCodEx/wiki/wiki/Markdown" target="_blank" rel="noreferrer">
50+
{caption}
51+
</a>
52+
),
53+
}}
54+
/>
55+
</Form.Text>
56+
</Col>
57+
</Row>
5558

56-
{showPreview && (
57-
<div className="mt-1">
58-
<div className={`${styles.preview} ${value.length === 0 ? styles.previewEmpty : ''}`}>
59-
{value.length === 0 && (
60-
<>
61-
(<FormattedMessage id="app.markdownTextArea.empty" defaultMessage="Empty" />)
62-
</>
63-
)}
64-
<Markdown source={value} />
65-
</div>
66-
</div>
59+
{showPreview && (
60+
<div className="mt-1">
61+
<div className={`${styles.preview} ${value.length === 0 ? styles.previewEmpty : ''}`}>
62+
{value.length === 0 && (
63+
<>
64+
(<FormattedMessage id="app.markdownTextArea.empty" defaultMessage="Empty" />)
65+
</>
66+
)}
67+
<Markdown source={value} />
68+
</div>
69+
</div>
70+
)}
71+
</>
6772
)}
6873
</div>
6974
);
@@ -77,6 +82,7 @@ MarkdownTextAreaField.propTypes = {
7782
value: PropTypes.string.isRequired,
7883
}).isRequired,
7984
disabled: PropTypes.bool,
85+
hideMarkdownPreview: PropTypes.bool,
8086
};
8187

8288
export default MarkdownTextAreaField;
Lines changed: 104 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from 'react';
1+
import React, { Component } from 'react';
22
import PropTypes from 'prop-types';
33
import { Form, FormGroup, FormLabel } from 'react-bootstrap';
44
import AceEditor from 'react-ace';
@@ -9,56 +9,107 @@ import { UserUIDataContext } from '../../../helpers/contexts.js';
99
// load the ACE editor only when rendering in the browser
1010
import { getAceModeFromExtension } from '../../helpers/syntaxHighlighting.js';
1111

12-
const SourceCodeField = ({
13-
input,
14-
mode,
15-
meta: { error, warning },
16-
label = null,
17-
children,
18-
tabIndex,
19-
onBlur,
20-
readOnly = false,
21-
...props
22-
}) => (
23-
<FormGroup controlId={input.name} className="mb-3">
24-
{Boolean(label) && (
25-
<FormLabel className={error ? 'text-danger' : warning ? 'text-warning' : undefined}>{label}</FormLabel>
26-
)}
27-
{canUseDOM && (
28-
<div className={readOnly ? 'noselection' : ''}>
29-
<UserUIDataContext.Consumer>
30-
{({ vimMode = false, darkTheme = true, editorFontSize = 16 }) => (
31-
<AceEditor
32-
{...props}
33-
{...input}
34-
mode={getAceModeFromExtension(mode)}
35-
theme={darkTheme ? 'monokai' : 'github'}
36-
name={input.name}
37-
tabIndex={tabIndex}
38-
keyboardHandler={vimMode ? 'vim' : undefined}
39-
width="100%"
40-
height="100%"
41-
minLines={5}
42-
maxLines={20}
43-
readOnly={readOnly}
44-
fontSize={editorFontSize}
45-
onBlur={
46-
() => input.onBlur() // this is a hack that will ensure blur call without distorting the contents
47-
}
48-
editorProps={{
49-
$blockScrolling: Infinity,
50-
$autoScrollEditorIntoView: false,
51-
}}
52-
/>
53-
)}
54-
</UserUIDataContext.Consumer>
55-
</div>
56-
)}
57-
{error && <Form.Text className="text-danger"> {error} </Form.Text>}
58-
{!error && warning && <Form.Text className="text-warning"> {warning} </Form.Text>}
59-
{children}
60-
</FormGroup>
61-
);
12+
const editorProps = {
13+
$blockScrolling: Infinity,
14+
$autoScrollEditorIntoView: false,
15+
};
16+
const digitCodes = {};
17+
for (let i = 0; i <= 9; ++i) {
18+
digitCodes['Digit' + i] = i;
19+
digitCodes['Numpad' + i] = i;
20+
}
21+
22+
class SourceCodeField extends Component {
23+
constructor() {
24+
super();
25+
this.focusedEditor = null;
26+
}
27+
28+
keyDownEventHandler = ev => {
29+
const { readOnly = false, getSnippet = null, setSnippet = null } = this.props;
30+
31+
if (
32+
this.focusedEditor &&
33+
!readOnly &&
34+
!ev.repeat &&
35+
!ev.shiftKey &&
36+
ev.ctrlKey &&
37+
digitCodes[ev.code] !== undefined
38+
) {
39+
if (ev.altKey) {
40+
const text = this.focusedEditor.getSelectedText() || this.focusedEditor.getValue();
41+
if (text) {
42+
setSnippet(digitCodes[ev.code], text);
43+
}
44+
} else {
45+
const snippet = getSnippet(digitCodes[ev.code]);
46+
if (snippet) {
47+
this.focusedEditor.insert(snippet);
48+
}
49+
}
50+
ev.stopPropagation();
51+
}
52+
};
53+
54+
focusHandler = (_, editor) => {
55+
this.focusedEditor = editor;
56+
};
57+
58+
blurHandler = () => {
59+
this.props.input.onBlur(); // this is a hack that will ensure blur call without distorting the contents
60+
this.focusedEditor = null;
61+
};
62+
63+
render() {
64+
const {
65+
input,
66+
mode,
67+
meta: { error, warning },
68+
label = null,
69+
children,
70+
tabIndex,
71+
onBlur,
72+
readOnly = false,
73+
...props
74+
} = this.props;
75+
return (
76+
<FormGroup controlId={input.name} className="mb-3">
77+
{Boolean(label) && (
78+
<FormLabel className={error ? 'text-danger' : warning ? 'text-warning' : undefined}>{label}</FormLabel>
79+
)}
80+
{canUseDOM && (
81+
<div className={readOnly ? 'noselection' : ''} onKeyDownCapture={this.keyDownEventHandler}>
82+
<UserUIDataContext.Consumer>
83+
{({ vimMode = false, darkTheme = true, editorFontSize = 16 }) => (
84+
<AceEditor
85+
{...props}
86+
{...input}
87+
mode={getAceModeFromExtension(mode)}
88+
theme={darkTheme ? 'monokai' : 'github'}
89+
name={input.name}
90+
tabIndex={tabIndex}
91+
keyboardHandler={vimMode ? 'vim' : undefined}
92+
width="100%"
93+
height="100%"
94+
minLines={5}
95+
maxLines={20}
96+
readOnly={readOnly}
97+
fontSize={editorFontSize}
98+
onBlur={this.blurHandler}
99+
editorProps={editorProps}
100+
onFocus={this.focusHandler}
101+
/>
102+
)}
103+
</UserUIDataContext.Consumer>
104+
</div>
105+
)}
106+
{error && <Form.Text className="text-danger"> {error} </Form.Text>}
107+
{!error && warning && <Form.Text className="text-warning"> {warning} </Form.Text>}
108+
{children}
109+
</FormGroup>
110+
);
111+
}
112+
}
62113

63114
SourceCodeField.propTypes = {
64115
input: PropTypes.shape({
@@ -76,6 +127,8 @@ SourceCodeField.propTypes = {
76127
label: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
77128
readOnly: PropTypes.bool,
78129
onBlur: PropTypes.func,
130+
getSnippet: PropTypes.func,
131+
setSnippet: PropTypes.func,
79132
};
80133

81134
export default SourceCodeField;

0 commit comments

Comments
 (0)