TextAreaEditor.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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 MarkdownListInterceptor from './MarkdownListInterceptor';
  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 MarkdownListInterceptor(),
  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. }
  70. /**
  71. * @inheritDoc
  72. */
  73. getStrFromBol() {
  74. }
  75. /**
  76. * @inheritDoc
  77. */
  78. getStrToEol() {
  79. }
  80. /**
  81. * @inheritDoc
  82. */
  83. replaceBolToCurrentPos(text) {
  84. }
  85. /**
  86. * keypress event handler
  87. * @param {string} event
  88. */
  89. keyPressHandler(event) {
  90. const key = event.key.toLowerCase();
  91. if (key === 'enter') {
  92. if (event.ctrlKey || event.altKey || event.metaKey) {
  93. return;
  94. }
  95. this.handleEnterKey();
  96. }
  97. }
  98. /**
  99. * handle ENTER key
  100. */
  101. handleEnterKey() {
  102. var context = {
  103. handlers: [], // list of handlers which process enter key
  104. editor: this,
  105. };
  106. const interceptorManager = this.interceptorManager;
  107. interceptorManager.process('preHandleEnter', context)
  108. .then(() => {
  109. if (context.handlers.length == 0) {
  110. // TODO impl
  111. // codemirror.commands.newlineAndIndentContinueMarkdownList(this.getCodeMirror());
  112. }
  113. });
  114. }
  115. /**
  116. * paste event handler
  117. * @param {any} event
  118. */
  119. pasteHandler(event) {
  120. const types = event.clipboardData.types;
  121. // text
  122. if (types.includes('text/plain')) {
  123. pasteHelper.pasteText(this, event);
  124. }
  125. // files
  126. else if (types.includes('Files')) {
  127. this.dispatchPasteFiles(event);
  128. }
  129. }
  130. dragEnterHandler(event) {
  131. this.dispatchDragEnter(event);
  132. }
  133. dispatchDragEnter(event) {
  134. if (this.props.onDragEnter != null) {
  135. this.props.onDragEnter(event);
  136. }
  137. }
  138. render() {
  139. return <React.Fragment>
  140. <FormControl
  141. componentClass="textarea" className="textarea-editor"
  142. inputRef={ref => { this.textarea = ref }}
  143. defaultValue={this.state.value}
  144. onChange={(e) => {
  145. if (this.props.onChange != null) {
  146. this.props.onChange(e.target.value);
  147. }
  148. }} />
  149. </React.Fragment>;
  150. }
  151. }
  152. TextAreaEditor.propTypes = Object.assign({
  153. }, AbstractEditor.propTypes);