react-simplemde-editor

授权协议 MIT License
开发语言 C/C++
所属分类 应用工具、 文档/文本编辑
软件类型 开源软件
地区 不详
投 递 者 宿衡虑
操作系统 跨平台
开源组织
适用人群 未知
 软件概览

React SimpleMDE (EasyMDE) Markdown Editor

React component wrapper forEasyMDE (the most fresh SimpleMDE fork).

Only two dependencies, React (peer) and EasyMDE (peer).

Built by @RIP21 ��‍��

Table of contents generated with markdown-toc

New in v5

  • [breaking] Full rewrite to hooks. Means more reactive so, probably, less bugs related with updates. Minimum React version required >=16.8.2
  • [breaking] easymde now a peer dependency, please install it manually
  • [breaking] label prop has been removed
  • [breaking] SSR safe nets removed, please make sure to import it dynamically
  • [breaking] options shall be memoized to prevent new instances from being created on each render and other related to that bugs (more on that below)
  • [potentially-breaking] Forwards ref, so you can easily get access to div wrapper by using ref prop.
  • [potentially-breaking] Lots of bugs fixed, examples updated
  • [potentially-breaking] @babel/runtime helpers are no longer inlined but imported.

Install

npm install --save react-simplemde-editor easymde

Note: Possibly you may need to install @babel/runtime, try without it, but if you don't have any issues, then you shouldn't.

Demo

Hosted demo

or to see it locally:

git clone https://github.com/RIP21/react-simplemde-editor.git
cd react-simplemde-editor
yarn install
yarn demo
open browser at localhost:3000

Usage

View the demo code for more examples.

All examples below are in TypeScript

Uncontrolled usage

import React from "react";
import SimpleMDE from "react-simplemde-editor";
import "easymde/dist/easymde.min.css";

<SimpleMDE />;

Controlled usage

export const ControlledUsage = () => {
  const [value, setValue] = useState("Initial value");

  const onChange = useCallback((value: string) => {
    setValue(value);
  }, []);

  return <SimpleMdeReact value={value} onChange={onChange} />;
};

Options

You can set API of SimpleMDE options which you pass down as a options prop.If you're using TypeScript it will be inferred by compiler.

Note: if you don't specify a custom id it will automatically generate an id for you.

Note that you need to useMemo to memoize options so they do not change on each rerender! It will affect behavior and performancebecause then on each render of the parent that renders SimpleMdeReact you'll get a new instance of the editor, which you definitely want to avoid!Also, if you change options on each value change you will lose focus.So, put options as a const outside of the component, or if options shall be partially or fully set by props make sure to useMemo incase of functional/hooks components, or class field for class based components.Slightly more on that here: #164

export const UsingOptions = () => {
  const [value, setValue] = useState("Initial");

  const onChange = useCallback((value: string) => {
    setValue(value);
  }, []);

  const autofocusNoSpellcheckerOptions = useMemo(() => {
    return {
      autofocus: true,
      spellChecker: false,
    } as SimpleMDE.Options;
  }, []);

  return (
    <SimpleMdeReact
      options={autofocusNoSpellcheckerOptions}
      value={value}
      onChange={onChange}
    />
  );
};

Hotkeys

You can include key maps using the extraKeys prop.Read more at CodeMirror extra keys

export const UpdateableByHotKeys = () => {
  const extraKeys = useMemo<KeyMap>(() => {
    return {
      Up: function (cm) {
        cm.replaceSelection(" surprise. ");
      },
      Down: function (cm) {
        cm.replaceSelection(" surprise again! ");
      },
    };
  }, []);

  const [value, setValue] = useState("initial");
  const onChange = (value: string) => setValue(value);

  return (
    <SimpleMdeReact value={value} onChange={onChange} extraKeys={extraKeys} />
  );
};

Custom preview rendering example

import ReactDOMServer from "react-dom/server";

export const CustomPreview = () => {
  const customRendererOptions = useMemo(() => {
    return {
      previewRender() {
        return ReactDOMServer.renderToString(
          <ReactMarkdown
            source={text}
            renderers={{
              CodeBlock: CodeRenderer,
              Code: CodeRenderer,
            }}
          />
        );
      },
    } as SimpleMDE.Options;
  }, []);

  return (
    <div>
      <h4>Custom preview</h4>
      <SimpleMdeReact options={customRendererOptions} />
    </div>
  );
};

Events / Additional event listeners for events of CodeMirror

See full list of events here

import { SimpleMdeReact } from "react-simplemde-editor";
import type { SimpleMdeToCodemirrorEvents } from "react-simplemde-editor";

export const CustomEventListeners = () => {
  const [value, setValue] = useState("Initial value");

  const onChange = useCallback((value: string) => {
    setValue(value);
  }, []);

  // Make sure to always `useMemo` all the `options` and `events` props to ensure best performance!
  const events = useMemo(() => {
    return {
      focus: () => console.log(value),
    } as SimpleMdeToCodemirrorEvents;
  }, []);

  return <SimpleMdeReact events={events} value={value} onChange={onChange} />;
};

Autosaving

export const Autosaving = () => {
  const delay = 1000;
  const autosavedValue = localStorage.getItem(`smde_demo`) || "Initial value";
  const anOptions = useMemo(() => {
    return {
      autosave: {
        enabled: true,
        uniqueId: "demo",
        delay,
      },
    };
  }, [delay]);

  return (
    <SimpleMdeReact id="demo" value={autosavedValue} options={anOptions} />
  );
};

Retrieve easymde, codemirror or cursor info to be able to manipulate it.

export const GetDifferentInstances = () => {
  // simple mde
  const [simpleMdeInstance, setMdeInstance] = useState<SimpleMDE | null>(null);

  const getMdeInstanceCallback = useCallback((simpleMde: SimpleMDE) => {
    setMdeInstance(simpleMde);
  }, []);

  useEffect(() => {
    simpleMdeInstance &&
      console.info("Hey I'm editor instance!", simpleMdeInstance);
  }, [simpleMdeInstance]);

  // codemirror
  const [codemirrorInstance, setCodemirrorInstance] = useState<Editor | null>(
    null
  );
  const getCmInstanceCallback = useCallback((editor: Editor) => {
    setCodemirrorInstance(editor);
  }, []);

  useEffect(() => {
    codemirrorInstance &&
      console.info("Hey I'm codemirror instance!", codemirrorInstance);
  }, [codemirrorInstance]);

  // line and cursor
  const [lineAndCursor, setLineAndCursor] = useState<Position | null>(null);

  const getLineAndCursorCallback = useCallback((position: Position) => {
    setLineAndCursor(position);
  }, []);

  useEffect(() => {
    lineAndCursor &&
      console.info("Hey I'm line and cursor info!", lineAndCursor);
  }, [lineAndCursor]);

  return (
    <div>
      <h4>Getting instance of Mde and codemirror and line and cursor info</h4>
      <SimpleMdeReact
        value="Go to console to see stuff logged"
        getMdeInstance={getMdeInstanceCallback}
        getCodemirrorInstance={getCmInstanceCallback}
        getLineAndCursor={getLineAndCursorCallback}
      />
    </div>
  );
};

Basic testing

Here is how you do it. It requires mock of certain browser pieces to work, but this is whole example.

import { act, render, screen } from "@testing-library/react";
import { useState } from "react";
import { SimpleMdeReact } from "SimpleMdeReact";
import userEvent from "@testing-library/user-event";

// @ts-ignore
Document.prototype.createRange = function () {
  return {
    setEnd: function () {},
    setStart: function () {},
    getBoundingClientRect: function () {
      return { right: 0 };
    },
    getClientRects: function () {
      return {
        length: 0,
        left: 0,
        right: 0,
      };
    },
  };
};

const Editor = () => {
  const [value, setValue] = useState("");
  return <SimpleMdeReact value={value} onChange={setValue} />;
};

describe("Renders", () => {
  it("succesfully", async () => {
    act(() => {
      render(<Editor />);
    });
    const editor = await screen.findByRole("textbox");
    userEvent.type(editor, "hello");
    expect(screen.getByText("hello")).toBeDefined();
  });
});

API

Props

export interface SimpleMdeReactProps
  extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> {
  id?: string;
  onChange?: (value: string) => void;
  value?: string;
  extraKeys?: KeyMap;
  options?: SimpleMDE.Options;
  events?: SimpleMdeToCodemirrorEvents;
  getMdeInstance?: (instance: SimpleMDE) => void;
  getCodemirrorInstance?: (codemirror: Editor) => void;
  getLineAndCursor?: (position: Position) => void;
}

All exports list

default - SimpleMdeReact

SimpleMdeReact - same as default but named

Types:

SimpleMdeReactProps - props of the component

DOMEvent - certain events that are used to get events exported below

CopyEvents - only copy codemirror events

GlobalEvents - some other global codemirror events

DefaultEvent - default codemirror event handler function

IndexEventsSignature - index signature that expects string as key and returns DefaultEvent

SimpleMdeToCodemirrorEvents - manually crafted events (based off @types/codemirror@0.0.109 that easymde uses internally) +all the above merged together into whole mapping between Codemirror event names and actual handlers forevents prop

GetMdeInstance - signature of the callback function that retrieves mde instance

GetCodemirrorInstance - signature of the callback function that retrieves codemirror instance

GetLineAndCursor - signature of the callback function that retrieves line and cursor info

Changelog

New in v4

  • Now uses EasyMDE (the most fresh SimpleMDE fork)instead of simplemde itself. Possible breaking changes, so I bumped version to v4.
  • One obvious breaking change. Is how CSS is have to be imported. It used to be simplemde/dist/simplemde.min.css now it will be easymde/dist/easymde.min.css

New in v3

  • The initialValue prop has been removed and replaced with a value prop, allowing direct changes to the value to be made after the component mounts.
  • v3.6.8 if rendering server-side, you can set static ids to avoid errors in rendering synchronization.
  • v3.6.17 TypeScript typings added.
  • v3.6.19 All props will be passed to the wrapper now (except a id, onChange and few others that are ignored)
  • v3.6.21 React 17 support (UNSAFE methods are no longer used)

New in v2

Version 1.0 did not have SimpleMDE options configured well, this readme reflects the changes made to better include options.This is still a very new project. Testing, feedback and PRs are welcome and appreciated.

  • 前言 有时,当我们使用react-router模块在路由切换时会出现页面空白的情况,这次就将该情况讲解清楚并解决。 出现的问题 下面,我们直接上有问题的代码: <Switch> <Route path={"/"} component={loadable(() => import("./App.jsx"))} /> <Route path={"/home"} component={l

  • 一、在 npm run build 的时候关闭 SourceMap 的方法 最不具破坏性且没有副作用的方案: 在根目录下新建 .env.production 文件; 然后在这个文件中只需写上一行代码: GENERATE_SOURCEMAP=false 如果因为 React 脚手架版本不支持,则可以使用以下方案,但是需要注意该方案根据不同版本会有所差异,且不会消除 CSS和 JS 文件中的 maps

  • CKEditor 4 页面多次调用 ,出现多个editor(编辑器),很无语 网上面有好多方法是用remove,其实是没有用的,比如: if (CKEDITOR.instances[id]){ CKEDITOR.remove(CKEDITOR.instances[id]); CKEDITOR.replace(id); } 解决办法: function Loa

  • 如果没有看前两篇的童鞋,可以看看前两篇文章的内容,虽然写的一般,但是起码也是一个重无到有的过程。 直接进主题 到这里,其实我们的前端页面布局,后台nodejs服务程序已经有了,现在就是写页面和调用后台服务程序去处理数据了。 现在可以看看练习项目的功能点了,其实很简单啦: 1、显示文章列表 2、添加文章,当然就包括:格式、图片、链接啦 3、简单的登录控制(这个超级简单,只是做了一个管理员账号的登录)

  • 最近在学nodejs,准备配合react+mongodb搭个博客,找了很多富文本编辑器,都不是很适合react用,后来看到一篇vue+node搭建博客的文章,里面使用的simplemde(github地址),完全就符合我的想法啊,界面简洁大方还有预览功能。 附上官方demo 用法也相当简单,官方介绍的是外链的引用方法,下面我说一下如何配合 makded 语法库和 highlight.js 代码高亮

 相关资料
  • SimpleMDE 是一个嵌入式 JavaScript 文本框,用于替代书写好看且易懂的 markdown。WYSIWYG-esque 编辑器可以让用户修改 markdown 的工具条按钮和快捷键。WYSIWYG 编辑器产生的 HTML 复杂且多 Bug。Markdown 有很多办法可以解决这个问题,但是在编辑的时候不是很清晰。SimpleMDE 可以为那些不太熟悉或者正在学习 markdown

  • Vue-SimpleMDE Markdown Editor component for Vue.js. Support only vue2.x. Use Setup No longer support Vue1.x, you can modify to use Install npm install vue-simplemde --save Use Internal reference in a

  • 问题内容: 我注意到可以这样导入: …或像这样: 第一个导入模块中的所有内容(请参阅:导入整个模块的内容) 第二个仅导入模块导出(请参阅:导入默认值) 似乎这两种方法是不同的,并且根本上是不兼容的。 为什么它们都起作用? 请参考源代码并解释该机制…我有兴趣了解其工作原理。 ES6常规模块信息回答了该问题。 我在问使模块像这样工作的机制。在这里,它似乎与源代码中的 “ hacky”导出机制有关,但尚

  • A simple markdown editor with preview, implemented with React.js and TypeScript. This React Component aims to provide a simple Markdown editor with syntax highlighting support. This is based on textar

  • react-markdown-editor 是 React.js 和 TypeScript 实现的 Markdown 编辑器。 Markdown 编辑器带预览功能 安装 npm i @uiw/react-markdown-editor 文档 文档实例预览:demo preview (����中国镜像网站) 使用 import MarkdownEditor from '@uiw/react-mark

  • 这篇快速上手指南会教你如何将TypeScript与React结合起来使用。 在最后,你将学到: 使用TypeScript和React创建工程 使用TSLint进行代码检查 使用Jest和Enzyme进行测试,以及 使用Redux管理状态 我们会使用create-react-app工具快速搭建工程环境。 这里假设你已经在使用Node.js和npm。 并且已经了解了React的基础知识。 我们之所以使