TextAreaEditor.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import FormControl from 'react-bootstrap/es/FormControl';
  4. import AbstractEditor from './AbstractEditor';
  5. import pasteHelper from './PasteHelper';
  6. import InterceptorManager from '../../../../lib/util/interceptor-manager';
  7. import PreventMarkdownListInterceptor from './PreventMarkdownListInterceptor';
  8. export default class TextAreaEditor extends AbstractEditor {
  9. constructor(props) {
  10. super(props);
  11. this.logger = require('@alias/logger')('growi:PageEditor:TextAreaEditor');
  12. this.state = {
  13. value: this.props.value,
  14. };
  15. this.init();
  16. this.handleEnterKey = this.handleEnterKey.bind(this);
  17. this.keyPressHandler = this.keyPressHandler.bind(this);
  18. this.pasteHandler = this.pasteHandler.bind(this);
  19. this.dragEnterHandler = this.dragEnterHandler.bind(this);
  20. }
  21. init() {
  22. this.interceptorManager = new InterceptorManager();
  23. this.interceptorManager.addInterceptors([
  24. new PreventMarkdownListInterceptor(),
  25. ]);
  26. }
  27. componentDidMount() {
  28. // initialize caret line
  29. this.setCaretLine(0);
  30. // set event handlers
  31. this.textarea.addEventListener('keypress', this.keyPressHandler);
  32. this.textarea.addEventListener('paste', this.pasteHandler);
  33. this.textarea.addEventListener('dragenter', this.dragEnterHandler);
  34. }
  35. /**
  36. * @inheritDoc
  37. */
  38. forceToFocus() {
  39. setTimeout(() => {
  40. this.textarea.focus();
  41. }, 150);
  42. }
  43. /**
  44. * @inheritDoc
  45. */
  46. setCaretLine(line) {
  47. if (isNaN(line)) {
  48. return;
  49. }
  50. // scroll to bottom
  51. this.textarea.scrollTop = this.textarea.scrollHeight;
  52. const lines = this.textarea.value.split('\n').slice(0, line+1);
  53. const pos = lines
  54. .map(lineStr => lineStr.length + 1) // correct length+1 of each lines
  55. .reduce((a, x) => a += x, 0) // sum
  56. - 1; // -1
  57. this.textarea.setSelectionRange(pos, pos);
  58. }
  59. /**
  60. * @inheritDoc
  61. */
  62. setScrollTopByLine(line) {
  63. // do nothing
  64. }
  65. /**
  66. * @inheritDoc
  67. */
  68. insertText(text) {
  69. const startPos = this.textarea.selectionStart;
  70. const endPos = this.textarea.selectionEnd;
  71. // create new value
  72. const value = this.textarea.value;
  73. const newValue = value.substring(0, startPos) + text + value.substring(endPos, value.length-1);
  74. // calculate new position
  75. const newPos = startPos + text.length;
  76. this.textarea.value = newValue;
  77. this.textarea.setSelectionRange(newPos, newPos);
  78. }
  79. /**
  80. * @inheritDoc
  81. */
  82. getStrFromBol() {
  83. }
  84. /**
  85. * @inheritDoc
  86. */
  87. getStrToEol() {
  88. }
  89. /**
  90. * @inheritDoc
  91. */
  92. replaceBolToCurrentPos(text) {
  93. }
  94. /**
  95. * keypress event handler
  96. * @param {string} event
  97. */
  98. keyPressHandler(event) {
  99. const key = event.key.toLowerCase();
  100. if (key === 'enter') {
  101. if (event.ctrlKey || event.altKey || event.metaKey) {
  102. return;
  103. }
  104. this.handleEnterKey();
  105. }
  106. }
  107. /**
  108. * handle ENTER key
  109. */
  110. handleEnterKey() {
  111. var context = {
  112. handlers: [], // list of handlers which process enter key
  113. editor: this,
  114. };
  115. const interceptorManager = this.interceptorManager;
  116. interceptorManager.process('preHandleEnter', context)
  117. .then(() => {
  118. if (context.handlers.length == 0) {
  119. // TODO impl
  120. // codemirror.commands.newlineAndIndentContinueMarkdownList(this.getCodeMirror());
  121. }
  122. });
  123. }
  124. /**
  125. * paste event handler
  126. * @param {any} event
  127. */
  128. pasteHandler(event) {
  129. const types = event.clipboardData.types;
  130. // text
  131. if (types.includes('text/plain')) {
  132. pasteHelper.pasteText(this, event);
  133. }
  134. // files
  135. else if (types.includes('Files')) {
  136. this.dispatchPasteFiles(event);
  137. }
  138. }
  139. dragEnterHandler(event) {
  140. this.dispatchDragEnter(event);
  141. }
  142. dispatchDragEnter(event) {
  143. if (this.props.onDragEnter != null) {
  144. this.props.onDragEnter(event);
  145. }
  146. }
  147. render() {
  148. return <React.Fragment>
  149. <FormControl
  150. componentClass="textarea" className="textarea-editor"
  151. inputRef={ref => { this.textarea = ref }}
  152. defaultValue={this.state.value}
  153. onChange={(e) => {
  154. if (this.props.onChange != null) {
  155. this.props.onChange(e.target.value);
  156. }
  157. }} />
  158. </React.Fragment>;
  159. }
  160. }
  161. TextAreaEditor.propTypes = Object.assign({
  162. }, AbstractEditor.propTypes);