textarea-markdown-editor v1.0.4
Textarea Markdown
npm install textarea-markdown-editor
Textarea Markdown is a simple markdown editor using only <textarea/>
. It extends textarea by adding formatting features like shortcuts, list-wrapping, invoked commands and other to make user experience better š
Essentially this library just provides the textarea Component. You can choose any markdown parser, create your own layout, and use your own textarea component that is styled and behaves however you like
Features
- Lists wrapping
- Auto formatting pasted links
- Indent tabulation
- 20 built-in customizable commands
Usage
import React, { Fragment, useRef, useState } from "react";
import TextareaMarkdown, { TextareaMarkdownRef } from "textarea-markdown-editor";
function App() {
const [value, setValue] = useState("");
const ref = useRef<TextareaMarkdownRef>(null);
return (
<Fragment>
<button onClick={() => ref.current?.trigger("bold")}>Bold</button>
<br />
<TextareaMarkdown ref={ref} value={value} onChange={(e) => setValue(e.target.value)} />
</Fragment>
);
}
ā¹ļø Ref instance provide the trigger
function to invoke commands
Custom textarea Component
You can use custom textarea Component. Just wrap it with TextareaMarkdown.Wrapper
import React, { useRef, useState } from "react";
import TextareaMarkdown, { TextareaMarkdownRef } from "textarea-markdown-editor";
import TextareaAutosize from "react-textarea-autosize";
function App() {
const [value, setValue] = useState("");
const ref = useRef<TextareaMarkdownRef>(null);
return (
<TextareaMarkdown.Wrapper ref={ref}>
<TextareaAutosize value={value} onChange={(e) => setValue(e.target.value)} />
</TextareaMarkdown.Wrapper>
);
}
ā¹ļø This solution will not create any real dom wrapper
Customize commands
You can specify or overwrite shortcuts for built-in commands or create your own
import React, { useRef, useState } from "react";
import TextareaMarkdown, { CommandHandler, TextareaMarkdownRef } from "textarea-markdown-editor";
/** Inserts š at the current position and select it */
const emojiCommandHandler: CommandHandler = ({ cursor }) => {
// MARKER - means a cursor position, or a selection range if specified two markers
cursor.insert(`${cursor.MARKER}š${cursor.MARKER}`);
};
function App() {
const [value, setValue] = useState("");
const ref = useRef<TextareaMarkdownRef>(null);
return (
<Fragment>
<button onClick={() => ref.current?.trigger("insert-emoji")}>Insert š</button>
<br />
<TextareaMarkdown
ref={ref}
value={value}
onChange={(e) => setValue(e.target.value)}
commands={[
{
name: "code",
shortcut: ["command+/", "ctrl+/"],
shortcutPreventDefault: true,
},
{
name: "insert-emoji",
handler: emojiCommandHandler,
},
]}
/>
</Fragment>
);
}
ā¹ļø Note that mutation element.value
will not trigger change
event on textarea element. Use cursor.setValue(...)
or other method of Cursor
.
ā¹ļø Mousetrap.js is used under the hood for shortcuts handling. It is great solution with simple and intuitive api. You can read more about combination in the documentation
š You can find more examples here
API
- TextareaMarkdownProps
- Command
- CommandHandler
- Built-in commands
- TextareaMarkdownOptions
- TextareaMarkdownRef
TextareaMarkdownProps
ā¹ļø TextareaMarkdown
accepts all props which native textarea supports
Property | Description | Type |
---|---|---|
options | Options config | TextareaMarkdownOptions |
commands | Array of commands configuration | Command [] |
Command
Name | Type | Description |
---|---|---|
name | TType | Built-in or custom command name |
shortcut? | string | string [] | Shortcut combinations (Mousetrap.js) |
shortcutPreventDefault? | boolean | Toggle key event prevent default:false |
handler? | CommandHandler | Handler function for custom commands |
enable? | boolean | Toggle command enabling |
CommandHandler
export type CommandHandler = (context: CommandHandlerContext) => void | Promise<void>;
export type CommandHandlerContext = {
textarea: HTMLTextAreaElement;
cursor: Cursor;
keyEvent?: KeyboardEvent;
clipboardEvent?: ClipboardEvent;
options: TextareaMarkdownOptions;
};
Built-in commands
Name | Description | Shortcut |
---|---|---|
bold | Insert or wrap bold markup | ctrl/command+b |
italic | Insert or wrap italic markup | ctrl/command+i |
strike-through | Insert or wrap strike-through markup | ctrl/command+shift+x |
link | Insert link markup | |
image | Insert image markup | |
unordered-list | Insert unordered list markup | |
ordered-list | Insert ordered list markup | |
code-block | Insert or wrap code block markup | |
code-inline | Insert or wrap inline code markup | |
code | Insert or wrap inline or block code markup dependent of selected | |
block-quotes | Insert block-quotes markup | |
h1 | Insert h1 headline | |
h2 | Insert h2 headline | |
h3 | Insert h3 headline | |
h4 | Insert h4 headline | |
h5 | Insert h5 headline | |
h6 | Insert h6 headline |
TextareaMarkdownOptions
Name | Type | Description |
---|---|---|
preferredBoldSyntax | "**" | "__" | Preferred bold wrap syntax default: '**' |
preferredItalicSyntax | "*" | "_" | Preferred italic wrap syntax default: '*' |
preferredUnorderedListSyntax | "-" | "*" | "+" | Preferred unordered list prefix default: '-' |
enableIndentExtension | boolean | Will handle tab and shift+tab keystrokes, on which will insert/remove indentation instead of the default behavior default:true |
enableLinkPasteExtension | boolean | Will handle paste event, on which will wrap pasted with link/image markup if pasted is URL default:true |
enablePrefixWrappingExtension | boolean | Will handle enter keystroke, on which will wrap current list sequence if needed default:true |
enableProperLineRemoveBehaviorExtension | boolean | Will handle command/ctrl+backspace keystrokes, on which will remove only a current line instead of the default behavior default:true |
customPrefixWrapping | (PrefixWrappingConfig | string )[] | Array of custom prefixes, that need to be wrapped. (Will not work with enablePrefixWrappingExtension:false ) |
blockQuotesPlaceholder | string | default: 'quote' |
boldPlaceholder | string | default: 'bold' |
codeBlockPlaceholder | string | default: 'code block' |
codeInlinePlaceholder | string | default: 'code' |
headlinePlaceholder | string | (level : number ) => string | default: (lvl) => 'headline ' + lvl |
imageTextPlaceholder | string | Used inside default image markup ![<example>](...) default: 'example' |
imageUrlPlaceholder | string | Used inside default image markup ![...](<image.png>) default: 'image.png' |
italicPlaceholder | string | default: 'italic' |
linkTextPlaceholder | string | Used inside default link markup [<example>](...) default: 'example' |
linkUrlPlaceholder | string | Used inside default image markup ![...](<url>) default: 'url' |
orderedListPlaceholder | string | default: 'ordered list' |
strikeThroughPlaceholder | string | default: 'strike through' |
unorderedListPlaceholder | string | default: 'unordered list' |
TextareaMarkdownRef
ā¹ļø Extends HTMLTextAreaElement
instance
trigger: (command: string) => void;
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago