Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/rip21/react-simplemde-editor
React wrapper for simplemde (easymde) markdown editor
https://github.com/rip21/react-simplemde-editor
easymde editor markdown marked marked-js react simplemde
Last synced: 29 days ago
JSON representation
React wrapper for simplemde (easymde) markdown editor
- Host: GitHub
- URL: https://github.com/rip21/react-simplemde-editor
- Owner: RIP21
- License: mit
- Created: 2015-12-10T05:27:33.000Z (almost 9 years ago)
- Default Branch: master
- Last Pushed: 2023-10-07T07:00:52.000Z (about 1 year ago)
- Last Synced: 2024-04-14T06:18:06.869Z (7 months ago)
- Topics: easymde, editor, markdown, marked, marked-js, react, simplemde
- Language: TypeScript
- Homepage: https://react-simplemde-edtior.netlify.com/
- Size: 6.76 MB
- Stars: 752
- Watchers: 8
- Forks: 102
- Open Issues: 11
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# React SimpleMDE (EasyMDE) Markdown Editor
[![NPM version][npm-badge]][npm]
React component wrapper for
[EasyMDE (the most fresh SimpleMDE fork)](https://github.com/Ionaru/easy-markdown-editor).Only two dependencies, React (peer) and EasyMDE (peer).
Built by [@RIP21](https://twitter.com/rip212) 👨💻
- [New in v5](#new-in-v5)
- [Install](#install)
- [Demo](#demo)
- [Usage](#usage)
- [Controlled usage](#controlled-usage)
- [Options](#options)
- [Hotkeys](#hotkeys)
- [Custom preview rendering example](#custom-preview-rendering-example)
- [Events / Additional event listeners for events of CodeMirror](#events--additional-event-listeners-for-events-of-codemirror)
- [Autosaving](#autosaving)
- [Retrieve `easymde`, `codemirror` or `cursor` info to be able to manipulate it.](#retrieve-easymde-codemirror-or-cursor-info-to-be-able-to-manipulate-it)
- [Basic testing](#basic-testing)
- [API](#api)
- [Props](#props)
- [All exports list](#all-exports-list)
- [Changelog](#changelog)
- [New in v4](#new-in-v4)
- [New in v3](#new-in-v3)
- [New in v2](#new-in-v2)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](https://react-simplemde-edtior.netlify.com/)
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](https://github.com/rip21/react-simplemde-editor/tree/master/src/demo) for more examples.
All examples below are in TypeScript
Uncontrolled usage
```tsx
import React from "react";
import SimpleMDE from "react-simplemde-editor";
import "easymde/dist/easymde.min.css";;
```### Controlled usage
```tsx
export const ControlledUsage = () => {
const [value, setValue] = useState("Initial value");const onChange = useCallback((value: string) => {
setValue(value);
}, []);return ;
};
```### Options
You can set API of [SimpleMDE options](https://github.com/Ionaru/easy-markdown-editor#configuration) 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 performance
because 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` in
case of functional/hooks components, or class field for `class` based components.
Slightly more on that here: [#164](https://github.com/RIP21/react-simplemde-editor/issues/164)```tsx
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 (
);
};
```### Hotkeys
You can include key maps using the `extraKeys` prop.
Read more at [CodeMirror extra keys](https://codemirror.net/doc/manual.html#option_extraKeys)```tsx
export const UpdateableByHotKeys = () => {
const extraKeys = useMemo(() => {
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 (
);
};
```### Custom preview rendering example
```tsx
import ReactDOMServer from "react-dom/server";export const CustomPreview = () => {
const customRendererOptions = useMemo(() => {
return {
previewRender() {
return ReactDOMServer.renderToString(
);
},
} as SimpleMDE.Options;
}, []);return (
Custom preview
);
};
```### Events / Additional event listeners for events of CodeMirror
See full list of events [here](https://codemirror.net/doc/manual.html#events)
```tsx
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 ;
};
```### Autosaving
```tsx
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 (
);
};
```### Retrieve `easymde`, `codemirror` or `cursor` info to be able to manipulate it.
```tsx
export const GetDifferentInstances = () => {
// simple mde
const [simpleMdeInstance, setMdeInstance] = useState(null);const getMdeInstanceCallback = useCallback((simpleMde: SimpleMDE) => {
setMdeInstance(simpleMde);
}, []);useEffect(() => {
simpleMdeInstance &&
console.info("Hey I'm editor instance!", simpleMdeInstance);
}, [simpleMdeInstance]);// codemirror
const [codemirrorInstance, setCodemirrorInstance] = useState(
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(null);const getLineAndCursorCallback = useCallback((position: Position) => {
setLineAndCursor(position);
}, []);useEffect(() => {
lineAndCursor &&
console.info("Hey I'm line and cursor info!", lineAndCursor);
}, [lineAndCursor]);return (
Getting instance of Mde and codemirror and line and cursor info
);
};
```### Basic testing
Here is how you do it. It requires mock of certain browser pieces to work, but this is whole example.
```tsx
import { act, render, screen } from "@testing-library/react";
import { useState } from "react";
import { SimpleMdeReact } from "react-simplemde-editor";
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 ;
};describe("Renders", () => {
it("succesfully", async () => {
act(() => {
render();
});
const editor = await screen.findByRole("textbox");
userEvent.type(editor, "hello");
expect(screen.getByText("hello")).toBeDefined();
});
});
```## API
### Props
```tsx
export interface SimpleMDEReactProps
extends Omit, "onChange"> {
id?: string;
onChange?: (value: string, changeObject?: EditorChange) => void;
value?: string;
extraKeys?: KeyMap;
options?: SimpleMDE.Options;
events?: SimpleMdeToCodemirrorEvents;
getMdeInstance?: GetMdeInstance;
getCodemirrorInstance?: GetCodemirrorInstance;
getLineAndCursor?: GetLineAndCursor;
placeholder?: string;
textareaProps?: Omit<
React.HTMLAttributes,
"id" | "style" | "placeholder"
>;
}
```### 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/[email protected]` that `easymde` uses internally) +
all the above merged together into whole mapping between Codemirror event names and actual handlers for
`events` 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)](https://github.com/Ionaru/easy-markdown-editor)
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.[npm-badge]: http://badge.fury.io/js/react-simplemde-editor.svg
[npm]: http://badge.fury.io/js/react-simplemde-editor