Package Exports
- textarea-markdown-editor
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (textarea-markdown-editor) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Basic Overview
Markdown textarea is a simple markdown UI headless editor using only <textarea/>. It extend textarea by adding formatting features like shortcuts, invoked commands, and other to make user experience better đ
Essentially this library - just provide textarea Component. You can choose any engine for markdown rendering, any layout. Can use any existing textarea Component and style it as you prefer
đ¯ Features
- Lists wrapping
- Auto formatting pasted links
- Indent tabulation
- 20 built-in customizable commands
Installation and usage
Quick Start âĄī¸
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>
<TextareaMarkdown ref={ref} value={value} onChange={(e) => setValue(e.target.value)} />
</Fragment>
);
}Customize commands
You can specify or overwrite shortcuts and toggle commands
import React, { useRef, useState } from "react";
import TextareaMarkdown, { TextareaMarkdownRef } from "textarea-markdown-editor";
function App() {
const [value, setValue] = useState("");
const ref = useRef<TextareaMarkdownRef>(null);
return (
<TextareaMarkdown
ref={ref}
value={value}
onChange={(e) => setValue(e.target.value)}
commands={[
{
name: "code",
shortcut: ["command+/", "ctrl+/"],
shortcutPreventDefault: true,
},
{
name: "indent",
enable: false,
},
]}
/>
);
}âšī¸ 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 if necessary
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
API
TextareaMarkdownProps
TextareaMarkdown Component props
âšī¸ extends HTMLTextAreaElement props
options?: TextareaMarkdownOptions;
commands?: CommandDefine[];TextareaMarkdownWrapperProps
TextareaMarkdown.Wrapper Component props
options?: TextareaMarkdownOptions;
commands?: CommandDefine[];TextareaMarkdownOptions
Option prop config
/** toggle auto wrapping with link markup when pasting the selected word */
useLinkMarkupOnSelectionPasteUrl?: boolean;
/** toggle tabulation lists prefix with content */
useIndentListPrefixTabulation?: boolean;
/** unordered list prefix syntax */
unorderedListSyntax?: "-" | "*";
/** bold wrapper syntax */
boldSyntax?: "**" | "__";
/** italic wrapper syntax */
italicSyntax?: "*" | "_";CommandDefine
Commands array item
name: string;
/** for custom commands */
handler?: CommandHandler;
shortcut?: string | string[];
shortcutPreventDefault?: boolean;
enable?: boolean;CommandHandler
Custom handler signature
export type CommandHandler = (context: CommandHandlerContext) => void | Promise<void> | Promise<string> | string;
export type CommandHandlerContext = {
element: HTMLTextAreaElement;
keyEvent?: KeyboardEvent;
options: TextareaMarkdownOptions;
};TextareaMarkdownRef
Ref TextareaMarkdown or TextareaMarkdown.Wrapper instance
âšī¸ extends HTMLTextAreaElement instance
trigger: (command: string) => void;Advanced usage đ§Ŧ
You can implement your own commands. For this you need to registry command by adding new item in commands array.
CommandConfig contain name, handler and optional shortcut.
Handler - invoked by trigger call or by pressing shortcuts and it make side effect with textarea.
Basically you can make with element whatever you want, but most likely you need to manipulate with content. For this
purpose you can use Cursor service. This wrapper combines content and selection manipulation and also proved calculated information
about position context and more.
import React, { Fragment, useRef, useState } from "react";
import TextareaMarkdown, { TextareaMarkdownRef, Cursor, CommandHandler } from "textarea-markdown-editor";
/** Inserts đ at the end of the line and select it */
const emojiCommandHandler: CommandHandler = ({ element }) => {
const cursor = new Cursor(element);
const currentLine = cursor.getLine();
// Cursor.$ - marker means cursor position, if specified two markers indicate a selection range
cursor.spliceContent(Cursor.raw`${currentLine} ${Cursor.$}đ${Cursor.$}`, {
replaceCount: 1, // replace current line
});
};
function App() {
const [value, setValue] = useState("");
const ref = useRef<TextareaMarkdownRef>(null);
return (
<Fragment>
<button onClick={() => ref.current?.trigger("insert-emoji")}>Insert đ</button>
<TextareaMarkdown
ref={ref}
value={value}
onChange={(e) => setValue(e.target.value)}
commands={[{ name: "insert-emoji", handler: emojiCommandHandler }]}
/>
</Fragment>
);
}
export default App;đ You can find more examples here
âšī¸ Note that mutation element.value will not trigger change event on textarea element. Use cursor.setValue(...)
or just return new content from handler.