All files Editor.tsx

95.45% Statements 21/22
93.33% Branches 28/30
100% Functions 7/7
100% Lines 19/19

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136                                                                                                            53x   53x 53x 53x 53x   53x             53x   38x               53x   38x                       53x 18x 18x     53x 16x 16x 16x       53x                                       53x                      
import React, { useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
import { PluggableList } from 'unified';
import { processHtml, htmlEncode } from './utils';
import shortcuts from './shortcuts';
import * as styles from './styles';
import './style/index.less';
 
export * from './SelectionText';
 
export interface TextareaCodeEditorProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
  prefixCls?: string;
  /**
   * Support dark-mode/night-mode
   */
  ['data-color-mode']?: 'dark' | 'light';
  /**
   * Set what programming language the code belongs to.
   */
  language?: string;
  /**
   * Optional padding for code. Default: `10`.
   */
  padding?: number;
  /**
   * rehypePlugins (Array.<Plugin>, default: `[[rehypePrism, { ignoreMissing: true }]]`)
   * List of [rehype plugins](https://github.com/rehypejs/rehype/blob/main/doc/plugins.md#list-of-plugins) to use. See the next section for examples on how to pass options
   */
  rehypePlugins?: PluggableList;
  /**
   * The minimum height of the editor. Default: `16`.
   */
  minHeight?: number;
  onKeyDown?: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void | boolean;
  /**
   * The number of spaces for indentation when pressing tab key. Default: `2`.
   */
  indentWidth?: number;
}
 
export default React.forwardRef<HTMLTextAreaElement, TextareaCodeEditorProps>((props, ref) => {
  const {
    prefixCls = 'w-tc-editor',
    value: _,
    padding = 10,
    minHeight = 16,
    placeholder,
    language,
    'data-color-mode': dataColorMode,
    className,
    style,
    rehypePlugins,
    onChange,
    indentWidth = 2,
    ...other
  } = props;
 
  const [value, setValue] = useState(props.value || '');
  useEffect(() => setValue(props.value || ''), [props.value]);
  const textRef = useRef<HTMLTextAreaElement>(null);
  useImperativeHandle<HTMLTextAreaElement, HTMLTextAreaElement>(ref, () => textRef.current!, [textRef]);
 
  const contentStyle = {
    paddingTop: padding,
    paddingRight: padding,
    paddingBottom: padding,
    paddingLeft: padding,
  };
 
  const htmlStr = useMemo(
    () =>
      processHtml(
        `<pre aria-hidden=true><code ${language && value ? `class="language-${language}"` : ''} >${htmlEncode(
          String(value || ''),
        )}</code><br /></pre>`,
        rehypePlugins,
      ),
    [value, language, rehypePlugins],
  );
  const preView = useMemo(
    () => (
      <div
        style={{ ...styles.editor, ...contentStyle, minHeight }}
        className={`${prefixCls}-preview ${language ? `language-${language}` : ''}`}
        dangerouslySetInnerHTML={{
          __html: htmlStr,
        }}
      />
    ),
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [prefixCls, language, htmlStr],
  );
 
  const change = (evn: React.ChangeEvent<HTMLTextAreaElement>) => {
    setValue(evn.target.value);
    onChange && onChange(evn);
  };
 
  const keyDown = (evn: React.KeyboardEvent<HTMLTextAreaElement>) => {
    Iif (other.readOnly) return;
    Eif (!other.onKeyDown || other.onKeyDown(evn) !== false) {
      shortcuts(evn, indentWidth);
    }
  };
 
  const textareaProps: React.TextareaHTMLAttributes<HTMLTextAreaElement> = {
    autoComplete: 'off',
    autoCorrect: 'off',
    spellCheck: 'false',
    autoCapitalize: 'off',
    ...other,
    placeholder,
    onKeyDown: keyDown,
    style: {
      ...styles.editor,
      ...styles.textarea,
      ...contentStyle,
      minHeight,
      ...(placeholder && !value ? { WebkitTextFillColor: 'inherit' } : {}),
    },
    onChange: change,
    className: `${prefixCls}-text`,
    value: value,
  };
 
  return (
    <div
      style={{ ...styles.container, ...style }}
      className={`${prefixCls} ${className || ''}`}
      data-color-mode={dataColorMode}
    >
      <textarea {...textareaProps} ref={textRef} />
      {preView}
    </div>
  );
});