react hook 版,第一版写的比较简单
效果图:
组件:
index.jsx 文件
import React, { useState, useEffect, useRef } from 'react'; import styles from './index.module.scss'; import PropTypes from 'prop-types'; const MyEditor = props => { // const getYAMLJS = require('yamljs'); // 需先安装插件,转换 .yaml 文件内容的数据类型 const { value, style, maxRow, onChange } = props; const [MyValue, setMyValue] = useState(""); const [top, setTop] = useState(0); const lineNumberBox = useRef(); useEffect(() => { if (onChange) { setMyValue(value); } }, [value]) const textareaChange = (ev) => { // 修改内容 if (onChange) { onChange(ev.target.value) } else { setMyValue(ev.target.value) } } const textareaScroll = (ev) => { // 获得滚动的距离 setTop(ev.target.scrollTop) } const renderList = () => { // 生成行号 const ary = []; for (let i = 1; i <= maxRow; i++) { ary.push(<li key={i}>{i}</li>) } return ary } return ( <div className={styles.myEditor}> <div style={style} className={styles.myEditorBox}> <span className={styles.lineNumber}> <ul ref={lineNumberBox} style={{ top: `-${top}px` }}> { renderList() } </ul> </span> <textarea id="textInner" className={styles.textInner} value={MyValue} onChange={textareaChange} onScroll={textareaScroll} style={{ resize: style.height || style.width ? "none" : "auto", maxHeight: `${maxRow * 24}px` }} /> </div> </div> ); }; MyEditor.defaultProps = { value: "", style: { height: "240px" }, maxRow: 1000, }; MyEditor.propTypes = { style: PropTypes.object, maxRow: PropTypes.number, // 和 input 一样, value 和 onChange 需同时使用,变成可控组件 value: PropTypes.string, onChange: PropTypes.func, }; export default MyEditor;
index.module.scss 样式文件
.myEditor { .myEditorBox{ // 我的编辑器 display: flex; justify-content: space-between; box-sizing: content-box; overflow: hidden; .lineNumber{ box-sizing: content-box; display: inline-block; color: #999; background-color: #333; min- 38px; border-radius: 3px 0 0 3px; text-align: right; overflow: hidden; position: relative; ul{ position: absolute; margin: 2px 0 0 0; padding: 0; 100%; li{ list-style-type: none; // 去掉 li 的默认样式 padding: 0 0.5em 0 0; 100%; line-height: 24px; } } } .textInner{ flex: 1; box-sizing: content-box; padding: 0; line-height: 24px; min-height: 24px; //最小高度 即 每行高度 === 父级 line-height max-height: 2400px; //最大高度 === 10倍 line-height 相当于最多展现10行 // 400px; background-color: #000; color: #ffffff; border-radius: 0 3px 3px 0; outline: none; // 去掉默认样式 } } }
可控组件的使用方式
import React, { useState, useEffect } from 'react'; import MyEditor from "@/components/MyEditor"; function DeveloperPage(props) { const [value, setvalue] = useState(``) const onChange = (value) => { setvalue(value) } return ( <div> <MyEditor style={{ "500px", height: "300px" }} value={value} onChange={onChange} // maxRow = 1000 /> </div> ); } export default DeveloperPage;