Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/mattcarlotta/react-smde

A lightweight, simple, markdown editor for React.
https://github.com/mattcarlotta/react-smde

component editor markdown markdown-editor react

Last synced: 2 days ago
JSON representation

A lightweight, simple, markdown editor for React.

Awesome Lists containing this project

README

        

# react-smde

A lightweight **Simple Markdown Editor** for React.

[![NPM](https://nodei.co/npm/react-smde.png)](https://nodei.co/npm/react-smde/)

[![codecov](https://codecov.io/gh/mattcarlotta/react-smde/branch/master/graph/badge.svg)](https://codecov.io/gh/mattcarlotta/react-smde) [![Open Issues](https://img.shields.io/github/issues-raw/mattcarlotta/react-smde)](https://github.com/mattcarlotta/react-smde/issues) [![Dependencies](https://img.shields.io/david/mattcarlotta/react-smde.svg)](https://david-dm.org/mattcarlotta/react-smde) [![License](https://img.shields.io/github/license/mattcarlotta/react-smde)](https://github.com/mattcarlotta/react-smde/blob/master/LICENSE)

[Installation](#installation)

[Demo](#demo)

[Basic Usage](#basic-usage)

[Props](#props)

[Markdown Previewing](#markdown-previewing)

[Package Exports](#package-exports)

[Hot Keys](#hot-keys)

[Custom Commands](#custom-commands)

[Custom Styling](#custom-styling)

[Suggestions](#suggestions)

[Tooltips](#tooltips)

[Builds](#builds)

[Report Bugs](#report-bugs)

[Feature Requests](#feature-requests)

[License](#license)

[Third Party Resources](#third-party-resources)

## Installation

```
npm i react-smde
```

or

```
yarn add react-smde
```

react-smde is unopinionated about how to preview Markdown, therefore you'll **need** to supply your own Markdown previewer (see [Basic Usage](#basic-usage) for an example).

## Demo

- [Live Demo](https://mattcarlotta.github.io/react-smde/)
- [Demo on CodeSandbox](https://codesandbox.io/s/react-smde-basic-example-9uo1o)
- Local demo (cloned repo) `npm run demo` or `yarn demo`

## Basic Usage

```jsx
import MDEditor from "react-smde";
import ReactMarkdown from "react-markdown";

class App extends Component {
constructor() {
super();
this.state = { value: "" };
this.handleValueChange = this.handleValueChange.bind(this);
}

handleValueChange(value) {
this.setState({ value });
}

render() {
return (

{this.state.value || "(empty)"}

);
}
}
```

## Props

The following props are accepted by `MDEditor`:

| `prop` | Description |
| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `autoGrow`(bool) | A `boolean` to autogrow the textarea until the `maxEditorHeight` has been reached. (default: `false`) |
| `classes`(obj) | An optional `object` of `string` classNames that will be appended to the specified className. (see [Custom Styling](#custom-styling) for more info) |
| `commands`(arr) | A single `array` with an array of grouped object commands. (see [Custom Commands](#custom-commands) for more info) |
| `debounceSuggestions`(num) | A `number` set in `ms` to debounce calling the `loadSuggestions` function. (default: `300`)† |
| `disableGrip`(bool) | A `boolean` to hide and disable the bottom textarea resizing button. (default: `false`) |
| `disableHotKeys`(bool) | A `boolean` to disable the textarea hot keys. (default: `true`) |
| `disablePreview`(bool) | A `boolean` to disable the preview button -- also disables the preview/write hot key. (default: `false`) |
| `disableToolbar`(bool) | A `boolean` to disable the toolbar. (default: `false`) |
| `hideGrip`(bool) | A `boolean` to hide the bottom textarea toolbar. (default: `false`) |
| `editorRef`(func) | An optional callback `function` to hoist the MDEditor's `ref`. |
| `grip`(str/node) | An optional custom grip `node` or `string` |
| `loadSuggestions`(func) | A `function` that returns an `array` of suggestions triggered by the `suggestionTriggerCharacter`. (see [Suggestions](#suggestions) for more info) |
| `maxCharacterLength`(num/str) | A maximum MDEditor character length as a `number` or `string`. (default: `null`) |
| `maxEditorHeight`(num/str) | A maximum MDEditor height `number` that is set in `px` or `string`. (default: `500`) |
| `maxEditorWidth` (num/str) | A maximum MDEditor width `number` or `string`. (default: `600px`) |
| `minEditorHeight`(num/str) | A minimum MDEditor height `number` that is set in `px` or `string`. (default: `250`) |
| `onChange`(func) | A **required** callback `function` to handle value changes. |
| `readOnly`(bool) | A `boolean` to disable editing the text within the textarea. (default: `false`) |
| `selectedTab`(str) | A `string` (`write`/`preview`) to initialize the MDEditor's view in. (default: `write`) |
| `showCharacterLength`(bool) | A `boolean` to display the MDEditor's character length. †† (default: `false`) |
| `suggestionTriggerCharacter`(str) | A `string` character to trigger suggestions. (default: `@`) |
| `textAreaProps`(obj) | An optional `object` of properties to apply to the textarea. |
| `tooltipPlacement`(str) | The tooltip position relative to the target. (default: `top` -- see [Tooltips](#tooltips) for more info) |
| `value`(str) | A **required** `string` value. |

† Setting `debounceSuggestions` lower than 300ms, will disable the suggestions loading indicator. In testing, a number lower than 300ms caused unavoidable UI flashes when the returned data is static. As such, this allows you to utilize an array of static data and avoid seeing a loading indicator for each key input.

†† Setting `showCharacterLength` as true will only be visible if `hideGrip` is false. In order words, if you hide the bottom bar, it won't display the character length.

## Markdown Previewing

The `MDEditor` is unopinionated when it comes to previewing markdown content. Therefore, you **must** supply your own Markdown previewer as `children` to the `MDEditor`. The demo provided in the source and the example below utilizes [react-markdown](https://github.com/rexxars/react-markdown).

```jsx
import React, { Component } from "react";
import MDEditor from "react-smde";
import ReactMarkdown from "react-markdown";

class App extends Component {
constructor() {
super();
this.state = { value: "" };
this.handleValueChange = this.handleValueChange.bind(this);
}

handleValueChange(value) {
this.setState({ value });
}

render() {
return (

{this.state.value || "(empty)"}

);
}
}
```

## Package Exports

Aside from the default exported `MDEditor`, this package also exports a few other **named** internals:

```
commands (an object of all predefined commands)
defaultCommandLayout (a chunked array of predefined commands)
replaceSelection (function to replace/insert text -- it requires two arguments: the editor ref and a string)
SvgIcon (component used for default command icons)
```

You can see use cases for these internals by visiting the [Live Demo](https://mattcarlotta.github.io/react-smde/).

## Hot Keys

The `MDEditor` comes pre-configured with disabled hot keys:

- Bold (ctrl+b)
- Italic (ctrl+i)
- Link (ctrl+k)
- Edit/Preview toggle (ctrl+0)

If you want to include these keys, then they can be enabled by passing the `disableHotKeys=false` prop to the `MDEditor`. By default, they're disabled because they can interfere with other key press triggered event listeners.

## Custom Styling

The `MDEditor` was designed to be as flexible as possible when it comes to customizing the appearance of the editor.

In order to change the style, pass a `classes` object property to the `MDEditor` with a custom class name targeting the specified property.

Click to view a summary of available "mde" property overrides...



mde (applied to root)
mdedropdown (applied to header dropdown)
mdetoolbar (applied to toolbar)
mdetoolbargroup (applied to toolbar groups)
mdetoolbaritem (applied to toolbar items)
mdetoolbarseparator (applied to toolbar separators)
mdepreview (applied to preview wrapper)
mdepreviewcontent (applied to previewed content)
mdenosuggestions (applied to no suggestions result item)
mdesuggestions (applied to suggestions overlay)
mdetextarea (applied to textarea input)
mdetextareawrapper (applied to textarea wrapper)
mdetextareacharacterlength (applied to textarea character length)
mdegripcontainer (applied to editor bottom grip container)
mdegripicon (applied to editor bottom grip icon)
mdetooltiparrow (tooltip arrow)
mdetooltippopper (tooltip container)
mdetooltippopperarrow (tooltip arrow container)
mdetooltip (tooltip)
mdetooltipplacementbottom (tooltips with placement bottom)
mdetooltipplacementleft (tooltips with placement left)
mdetooltipplacementright (tooltips with placement right)
mdetooltipplacementtop (tooltips with placement top)
mdetooltiptouch (tooltip that has been activated by touch)


For example:

```
classes={{ mde: "custom-mde", mdetoolbar: "custom-toolbar" }}
```

Note: This package uses `styled-components` under the hood and your CSS classnames will need to have higher specificity when overriding styles. See [issues with specificity](https://styled-components.com/docs/advanced#issues-with-specificity) for more information.


## Custom Commands

You can rearrange, remove, and adjust properties and/or add your own commands! The `commands` property of the `MDEditor` expects a single array of one or many arrays of grouped object commands.

Commands are simple objects where `name` must either match a name from this predefined [list](src/commands/index.js#L23-L38) or must be a unique string:

```
{
name: "bold",
tooltip: "Add bold text (ctrl+b)",
buttonProps: { "aria-label": "Add bold text" },
icon:
}
```

The `icon` property must be a React node or a string. You can either pass your own node or you can import the `SvgIcon` from this package and pass it an `icon` string property as shown above. For predefined icons, please see this [function](src/icons/index.js#L286-L325), which returns a predefined React SVG node based upon a string.

You can override button commands by passing in a callback function to the the `buttonProps`. This assumes that your button is not a menu. If it is a menu, then you can pass an `onClick` callback function as a property and it will override the `children`'s button commands. For a working example, see the [Custom Commands Demo](https://mattcarlotta.github.io/react-smde/?path=/story/mdeditor--custom-commands-example).

If you wish to append to the predefined commands, then you can import the `defaultCommandLayout` from the package and spread it out in the `commands` property (see example below).

For example:

```jsx
import MDEditor, { commands, defaultCommandLayout, SvgIcon } from "react-smde";

const { checkedList, orderedList, unorderedList } = commands;

// manual command layout

}
]
]
}
...otherProps
>
...etc

// appending custom commands to the default layout

}
]
]
}
...otherProps
>
...etc

```

## Suggestions

In order to use suggestions, you must supply a callback function to the `MDEditor` as `loadSuggestions`. The `loadSuggestions` callback function **must** return data in the following structure:

```
[
{ value: "example1" },
{ value: "example2" },
{ value: "example3" },
...etc
]
```

By default, the `MDEditor` expects the data to be filtered server-side or by the `loadSuggestions` callback function.

The suggestions overlay will only be triggered by the `suggestionTriggerCharacter` and will only execute the `loadSuggestions` function as determined by the `debounceSuggestions` property.

Please note that setting a `debounceSuggestions` lower than `300`ms will disable the loading indicator -- this is useful if the returned data remains static.

For a dynamic data set example, see the [Demo](#demo) example above, otherwise here's a static data example:

```jsx
import React, { Component } from "react";
import MDEditor from "react-smde";

class App extends Component {
constructor() {
super();
this.state = {
value: ""
suggestions: [
{ value: "andre" },
{ value: "angela" },
{ value: "david" },
{ value: "louise" }
]
};
this.handleValueChange = this.handleValueChange.bind(this);
this.loadSuggestions = this.loadSuggestions.bind(this);
}

handleValueChange(value) {
this.setState({ value });
}

loadSuggestions(searchText) {
return this.state.suggestions.filter(({ value }) =>
value.toLowerCase().includes(searchText.toLowerCase())
);
}

render() {
return(



{this.state.value}


);
}
}
```

## Tooltips

You can specify the position of the tooltip in relation to its target. For example, one of the following `string`s can be passed to the `tooltipPlacement` property:

```
'bottom-end'
'bottom-start'
'bottom'
'left-end'
'left-start'
'left'
'right-end'
'right-start'
'right'
'top-end'
'top-start'
'top'
```

Please note that there must be sufficient surrounding window space for the tooltip to occupy the specified placement area; otherwise, the tooltip location may be incorrectly calculated and cause an undesirable UX.

## Builds

By default, this package is compiled to a `common-js` (CJS - `main`) file with supplemental `Universal Module Definition` (UMD - `fallback`) and a `ECMAScript Module` (ESM - `module`) files. If you wish to use one of the supplemental verisons, then you can do so by either:

**Option 1**: Import from `react-smde/dist/(esm|umd)/index.(esm.umd).js`.

**Option 2**: If you're using webpack, then you can utilize the `resolve.mainFields` property and point to one of the fields listed above: `main`, `module`, or `fallback`. See the **resolve.mainFields** webpack documentation for more info.

## Report bugs

If you run into any issues, please fill out a bug report.

**⚠️ NOTE**: Please provide a reproducible codesandbox example of the bug(s) you're experiencing. Issues that don't provide a reproducible example **may be ignored.**

## Feature Requests

Have a feature you want included or believe the editor is missing a standard feature? You can either fork the repo, commit changes, and submit a new PR (please include relevant `.tests.js` files and run `npm run test:cov` or `yarn test:cov` to make sure the code is covered) or you can submit a feature request.

## License

react-smde is [MIT licensed](LICENSE).

## Third Party Resources

In order to make react-smde, the following packages are referenced and used within this package:

- https://github.com/grassator/insert-text-at-cursor
- https://github.com/JedWatson/classnames
- https://github.com/andrerpena/react-mde
- https://github.com/mui-org/material-ui
- https://github.com/styled-components/styled-components
- https://github.com/lakebeach/rollup-plugin-local-resolving