当前位置: 首页 > 工具软件 > monaco-editor > 使用案例 >

monaco-editor 编辑器 react版本简单理解

谢高峯
2023-12-01

1,下载安装依赖 

我直接用的基于react的继承依赖,也可以用官网的。

npm install @monaco-editor/react

官网链接:monaco-deitor编辑器官网

npm install monaco-editor

2,创建组件实例

import Editor from '@monaco-editor/react';

interface Props {
  value?: string | undefined;
  changeValue?: (val: string | undefined) => void;
  height?: number;
  isREadOnly?: boolean;
}

const GraphEditor = ({ value, height, changeValue, isREadOnly }: Props) => {
  return (
    <div>
      <Editor
        language="typescript"
        width="100%"
        value={value}
        height={height || 600}
        defaultValue='const aaa="bbbbxxx"'
        onChange={(value: string | undefined) => {
          if (changeValue) changeValue(value);
        }}
        options={{
          theme: 'vs', // 编辑器主题颜色
          folding: true, // 是否折叠
          foldingHighlight: true, // 折叠等高线
          foldingStrategy: 'indentation', // 折叠方式  auto | indentation
          showFoldingControls: 'always', // 是否一直显示折叠 always | mouseover
          disableLayerHinting: true, // 等宽优化
          emptySelectionClipboard: false, // 空选择剪切板
          selectionClipboard: false, // 选择剪切板
          automaticLayout: true, // 自动布局
          codeLens: false, // 代码镜头
          scrollBeyondLastLine: false, // 滚动完最后一行后再滚动一屏幕
          colorDecorators: true, // 颜色装饰器
          accessibilitySupport: 'off', // 辅助功能支持  "auto" | "off" | "on"
          lineNumbers: 'on', // 行号 取值: "on" | "off" | "relative" | "interval" | function
          lineNumbersMinChars: 5, // 行号最小字符   number
          readOnly: isREadOnly, //是否只读  取值 true | false
        }}
      />
    </div>
  );
};
export default GraphEditor;

在其他组件使用直接引入组件就好

获取在线编辑的信息 : 在处理数据时候,拿传入的value。在onchange里面也能获取到

eg: 右侧可拖拽左侧文件夹的一个测试小demo

import { Input, Popconfirm, Popover, Tree } from 'antd';
import React, { useCallback, useEffect, useState } from 'react';
import styles from './index.less';
// import {debounce} from 'lodash'
// import { useDebounceFn } from '@ant-design/pro-components';
import GraphEditor from '@/components/GraphEditor/editorReact';
import {
  CloseCircleOutlined,
  DragOutlined,
  FolderAddOutlined,
  MoreOutlined,
  PlusOutlined,
  SearchOutlined,
} from '@ant-design/icons';

const FuwuDemo = () => {
  const { DirectoryTree } = Tree;
  const [isDrap, setIsDrap] = useState<boolean>(false); // 是否在拖拽
  const [dataSetHeight, setDatasetHeight] = useState<number>(); // 高度
  const [isHover, setIsHover] = useState<boolean>(false); // 是否悬浮
  const [textTitle, setTextTitle] = useState<any>(); // 切换面板内容
  const [changeValue, setChanneValue] = useState<string>(''); // 搜索内容
  const [selectKey, setSelectKey] = useState<string[]>([]); // 文件展开key
  const [defauSelectKey, setDefauSelectKey] = useState<string[]>([]); // 点击清空搜索返回之前的状态
  const [dragMask] = useState<HTMLDivElement>(() => {
    const newMask = document.createElement('div');
    newMask.className = styles.dragMask;
    newMask.style.display = 'none';
    document.body.append(newMask);
    return newMask;
  });
  const [editIcon, setEditIcon] = useState<any>({});
  const [isEdit, setIsEdit] = useState<any>({});
  const treeData = [
    {
      title: 'parent 0',
      key: '0-0',
      children: [
        { title: 'leaf 0-0', key: '0-0-0', isLeaf: true },
        { title: 'leaf 0-1', key: '0-0-1', isLeaf: true },
        { title: '嗨啊嗨', key: '0-0-2', isLeaf: true },
      ],
    },
    {
      title: 'parent 1',
      key: '0-1',
      children: [
        { title: 'leaf 1-0', key: '0-1-0', isLeaf: true },
        { title: 'leaf 1-1', key: '0-1-1', isLeaf: true },
      ],
    },
  ];
  const onSelect = (keys: React.Key[], info: any) => {
    setTextTitle(info);
    if (!info?.node?.children) {
      return;
    }
  };

  useEffect(() => {
    const [min, max] = [10, 400];
    const onMouseMove = (e: MouseEvent) => {
      if (isDrap) {
        const target = document.querySelector('.bbBox'); // 获取dom实例
        if (target) {
          let panelHeight = e.pageY - target.getBoundingClientRect().top;
          if (panelHeight < min) {
            panelHeight = min;
          }
          if (panelHeight > max) {
            panelHeight = max;
          }
          setDatasetHeight(panelHeight);
        }
      }
    };

    const onMouseUp = () => {
      setIsDrap(false);
      dragMask.style.display = 'none';
    };

    document.body.addEventListener('mousemove', onMouseMove);
    document.body.addEventListener('mouseup', onMouseUp);
    return () => {
      document.body.removeEventListener('mousemove', onMouseMove);
      document.body.removeEventListener('mouseup', onMouseUp);
    };
  }, [isDrap, setIsDrap]);
  const aaas = () => {
    setIsDrap(true);
    dragMask.style.display = 'block';
  };
  const deleteEdit = (e: any, nodeData: any) => {
    e.stopPropagation();
    console.log(nodeData, 'nodeData');
  };
  const content = useCallback((nodeData: any) => {
    return (
      <div
        onClick={(e) => {
          e.stopPropagation();
        }}
        className={styles.exitButtonBox}
      >
        <Popconfirm
          onConfirm={(e) => {
            deleteEdit(e, nodeData);
          }}
          title={() => {
            return (
              <div>
                <h3>
                  {nodeData?.children
                    ? '确认删除这个文件夹吗'
                    : '你确定要删除吗?'}
                </h3>
                {nodeData?.children ? (
                  <div
                    style={{
                      fontSize: '14px',
                      color: 'rgba(0,10,26,0.68)',
                      marginTop: '12px',
                    }}
                  >
                    {nodeData?.children?.length
                      ? '删除后,该文件夹下的查询也将删除,不可恢复。'
                      : '删除后,该文件夹不可恢复。'}
                  </div>
                ) : null}
              </div>
            );
          }}
        >
          <p className={styles.buttonIcon}>删除</p>
        </Popconfirm>

        <p
          className={styles.buttonIcon}
          onClick={(e) => {
            e.stopPropagation();
            setIsEdit(nodeData);
          }}
          style={{ marginBottom: '-1px' }}
        >
          重命名
        </p>
      </div>
    );
  }, []);

  const editEnter = (e: any, nodeData: any) => {
    // 编辑操作调用接口
    console.log(e.target.value, '11111value');
    console.log(nodeData, 'nodeData22222');
  };
  const testfun = (str: string, key: string) => {
    // 搜索富文本替换
    if (!str?.includes(key)) {
      return;
    }
    let reg = new RegExp(key, 'g');
    let newst = str.replace(reg, '<font color=black>' + key + '</font>');
    return newst;
  };
  const childrenRender = useCallback(
    (nodeData: any) => {
      const xrarr = testfun(nodeData?.title, changeValue);
      return (
        <div
          style={{ width: '100%', height: '30px' }}
          onMouseMove={() => {
            setEditIcon(nodeData);
          }}
          onMouseLeave={() => {
            setEditIcon({});
          }}
        >
          {isEdit?.key === nodeData?.key ? (
            <Input
              style={{ float: 'left', width: '60%' }}
              defaultValue={nodeData?.title}
              onClick={(e) => {
                e.stopPropagation();
              }}
              onBlur={() => {
                setIsEdit({});
              }}
              onPressEnter={(e: any) => {
                editEnter(e, nodeData);
              }}
              ref={function (input) {
                if (input !== null) {
                  input.focus();
                }
              }}
            />
          ) : (
            <div
              className={styles.fwbBoox}
              dangerouslySetInnerHTML={{ __html: xrarr ?? nodeData?.title }}
            ></div>
          )}
          {editIcon?.key === nodeData?.key ? (
            <div style={{ float: 'right', marginTop: '3px' }}>
              <PlusOutlined
                style={{ marginRight: '20px' }}
                onClick={(e) => {
                  e.stopPropagation();
                }}
                title="新建查询"
              />
              <Popover
                overlayClassName={styles.propBox}
                content={content(nodeData)}
                trigger="click"
              >
                <MoreOutlined
                  style={{ color: 'black', marginTop: '4px' }}
                  onClick={(e) => {
                    e.stopPropagation();
                  }}
                />
              </Popover>
            </div>
          ) : null}
        </div>
      );
    },
    [changeValue, editIcon, isEdit],
  );
  const onExpand = (expandedKeys: any) => {
    setSelectKey(expandedKeys);
    setDefauSelectKey(expandedKeys);
  };
  const getTreeParents = (
    data: any,
    changeValues: string,
    parentKey: string[] = [],
  ) => {
    data.forEach((item: any) => {
      if (item.children && item.children.length) {
        const hildrenType = item?.children?.find((itType: any) => {
          return itType?.title?.includes(changeValues);
        });
        if (hildrenType) {
          parentKey.push(item?.key);
        }
        selectKey?.forEach((keyItem) => {
          if (item?.title?.includes(changeValues) && keyItem === item?.key) {
            parentKey.push(item?.key);
          }
        });
        const temp = getTreeParents(item.children, changeValues);
        if (temp.length) {
          parentKey.push(...temp);
        }
      }
    });
    return parentKey;
  };

  return (
    <div style={{ width: '100%' }}>
      <div className={styles.treeBox}>
        <div className={styles.leftBox}>
          <div className={styles.iconKaifaBox}>
            开发调试 <FolderAddOutlined className={styles.addIcon} />
          </div>
          <Input
            style={{ marginTop: '16px' }}
            prefix={<SearchOutlined />}
            placeholder="请输入关键词搜索"
            onChange={(e) => {
              setChanneValue(e.target.value);
              if (!e.target.value) {
                return;
              }
              const parentKey = getTreeParents(treeData, e.target.value);
              setSelectKey(Array.from(new Set(parentKey)));
            }}
            allowClear={{
              clearIcon: (
                <CloseCircleOutlined
                  onClick={() => {
                    setSelectKey(defauSelectKey);
                  }}
                />
              ),
            }}
          />
          <DirectoryTree
            expandedKeys={selectKey}
            multiple
            style={{ marginTop: '16px' }}
            blockNode
            onSelect={onSelect}
            onExpand={onExpand}
            treeData={treeData}
            titleRender={childrenRender}
          />
        </div>
        <div
          style={{ height: '100%', width: '100%', backgroundColor: 'white' }}
          className="bbBox"
        >
          <div
            style={{
              height: dataSetHeight ?? '40%',
              width: '100%',
              backgroundColor: 'white',
              overflowY: 'auto',
            }}
          >
            <div
              style={{
                maxHeight: '400px',
                paddingTop: '24px',
              }}
            >
              <GraphEditor isREadOnly={false} height={400} />
            </div>
          </div>
          <div
            className={styles.iconYidong}
            onMouseDown={() => {
              aaas();
            }}
            onMouseMove={() => {
              setIsHover(true);
            }}
            onMouseLeave={() => {
              setIsHover(false);
            }}
          >
            {isHover || isDrap ? (
              <DragOutlined className={styles.iconYii} />
            ) : null}
          </div>
          <div
            style={{
              height: dataSetHeight ? 600 - dataSetHeight : '40%',
              width: '100%',
              backgroundColor: 'white',
            }}
          />
        </div>
      </div>
    </div>
  );
};

export default FuwuDemo;

 类似资料: