index.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. // jshint ignore:start
  2. import React from 'react';
  3. import Code from './actions/code';
  4. import Emphasis from './actions/emphasis';
  5. import Hr from './actions/hr';
  6. import Image from './actions/image';
  7. import Link from './actions/link';
  8. import Striketrough from './actions/striketrough';
  9. import Strong from './actions/strong';
  10. import Quote from './actions/quote';
  11. import AttachmentsEditor from './attachments';
  12. import Upload from './attachments/upload-button/';
  13. import MarkupPreview from './markup-preview';
  14. import * as textUtils from './textUtils';
  15. import Button from 'misago/components/button';
  16. import misago from 'misago';
  17. import ajax from 'misago/services/ajax';
  18. import modal from 'misago/services/modal';
  19. import snackbar from 'misago/services/snackbar';
  20. export default class extends React.Component {
  21. constructor(props) {
  22. super(props);
  23. this.state = {
  24. isPreviewLoading: false
  25. };
  26. }
  27. onPreviewClick = () => {
  28. if (this.state.isPreviewLoading) {
  29. return;
  30. }
  31. this.setState({
  32. isPreviewLoading: true
  33. });
  34. ajax.post(misago.get('PARSE_MARKUP_API'), {post: this.props.value}).then((data) => {
  35. modal.show(
  36. <MarkupPreview markup={data.parsed} />
  37. );
  38. this.setState({
  39. isPreviewLoading: false
  40. });
  41. }, (rejection) => {
  42. if (rejection.status === 400) {
  43. snackbar.error(rejection.detail);
  44. } else {
  45. snackbar.apiError(rejection);
  46. }
  47. this.setState({
  48. isPreviewLoading: false
  49. });
  50. });
  51. };
  52. replaceSelection = (operation) => {
  53. operation(textUtils.getSelectionText(), this._replaceSelection);
  54. };
  55. _replaceSelection = (newValue) => {
  56. this.props.onChange({
  57. target: {
  58. value: textUtils.replace(newValue)
  59. }
  60. });
  61. };
  62. render() {
  63. return (
  64. <div className="editor-border">
  65. <textarea
  66. className="form-control"
  67. defaultValue={this.props.value}
  68. disabled={this.props.loading}
  69. id="editor-textarea"
  70. onChange={this.props.onChange}
  71. rows="9"
  72. ></textarea>
  73. <div className="editor-footer">
  74. <Strong
  75. className="btn-default btn-sm pull-left"
  76. disabled={this.props.loading || this.state.isPreviewLoading}
  77. replaceSelection={this.replaceSelection}
  78. />
  79. <Emphasis
  80. className="btn-default btn-sm pull-left"
  81. disabled={this.props.loading || this.state.isPreviewLoading}
  82. replaceSelection={this.replaceSelection}
  83. />
  84. <Striketrough
  85. className="btn-default btn-sm pull-left"
  86. disabled={this.props.loading || this.state.isPreviewLoading}
  87. replaceSelection={this.replaceSelection}
  88. />
  89. <Hr
  90. className="btn-default btn-sm pull-left"
  91. disabled={this.props.loading || this.state.isPreviewLoading}
  92. replaceSelection={this.replaceSelection}
  93. />
  94. <Link
  95. className="btn-default btn-sm pull-left"
  96. disabled={this.props.loading || this.state.isPreviewLoading}
  97. replaceSelection={this.replaceSelection}
  98. />
  99. <Image
  100. className="btn-default btn-sm pull-left"
  101. disabled={this.props.loading || this.state.isPreviewLoading}
  102. replaceSelection={this.replaceSelection}
  103. />
  104. <Quote
  105. className="btn-default btn-sm pull-left"
  106. disabled={this.props.loading || this.state.isPreviewLoading}
  107. replaceSelection={this.replaceSelection}
  108. />
  109. <Code
  110. className="btn-default btn-sm pull-left"
  111. disabled={this.props.loading || this.state.isPreviewLoading}
  112. replaceSelection={this.replaceSelection}
  113. />
  114. <Upload
  115. className="btn-default btn-sm pull-left"
  116. disabled={this.props.loading || this.state.isPreviewLoading}
  117. />
  118. <Button
  119. className="btn-default btn-sm pull-left"
  120. disabled={this.props.loading || this.state.isPreviewLoading}
  121. onClick={this.onPreviewClick}
  122. type="button"
  123. >
  124. {gettext("Preview")}
  125. </Button>
  126. <Button
  127. className="btn-primary btn-sm pull-right"
  128. loading={this.props.loading}
  129. >
  130. {this.props.submitLabel || gettext("Post")}
  131. </Button>
  132. <button
  133. className="btn btn-default btn-sm pull-right"
  134. disabled={this.props.loading}
  135. onClick={this.props.onCancel}
  136. type="button"
  137. >
  138. {gettext("Cancel")}
  139. </button>
  140. <Protect
  141. canProtect={this.props.canProtect}
  142. disabled={this.props.loading}
  143. onProtect={this.props.onProtect}
  144. onUnprotect={this.props.onUnprotect}
  145. protect={this.props.protect}
  146. />
  147. </div>
  148. <AttachmentsEditor
  149. attachments={this.props.attachments}
  150. onAttachmentsChange={this.props.onAttachmentsChange}
  151. placeholder={this.props.placeholder}
  152. replaceSelection={this.replaceSelection}
  153. />
  154. </div>
  155. );
  156. }
  157. }
  158. export function Protect(props) {
  159. if (props.canProtect) {
  160. return (
  161. <button
  162. className="btn btn-icon btn-default btn-sm pull-right"
  163. disabled={props.disabled}
  164. onClick={props.protect ? props.onUnprotect : props.onProtect}
  165. title={props.protect ? gettext('Protected') : gettext('Protect')}
  166. type="button"
  167. >
  168. <span className="material-icon">
  169. {props.protect ? 'lock' : 'lock_outline'}
  170. </span>
  171. </button>
  172. );
  173. } else {
  174. return null;
  175. }
  176. }