1.0.4 ā€¢ Published 2 years ago

textarea-markdown-editor v1.0.4

Weekly downloads
-
License
MIT
Repository
-
Last release
2 years ago

Textarea Markdown

CI status codecov NPM version

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

ā„¹ļø TextareaMarkdown accepts all props which native textarea supports

PropertyDescriptionType
optionsOptions configTextareaMarkdownOptions
commandsArray of commands configurationCommand[]

Command

NameTypeDescription
nameTTypeBuilt-in or custom command name
shortcut?string | string[]Shortcut combinations (Mousetrap.js)
shortcutPreventDefault?booleanToggle key event prevent default:false
handler?CommandHandlerHandler function for custom commands
enable?booleanToggle 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

NameDescriptionShortcut
boldInsert or wrap bold markupctrl/command+b
italicInsert or wrap italic markupctrl/command+i
strike-throughInsert or wrap strike-through markupctrl/command+shift+x
linkInsert link markup
imageInsert image markup
unordered-listInsert unordered list markup
ordered-listInsert ordered list markup
code-blockInsert or wrap code block markup
code-inlineInsert or wrap inline code markup
codeInsert or wrap inline or block code markup dependent of selected
block-quotesInsert block-quotes markup
h1Insert h1 headline
h2Insert h2 headline
h3Insert h3 headline
h4Insert h4 headline
h5Insert h5 headline
h6Insert h6 headline

TextareaMarkdownOptions

NameTypeDescription
preferredBoldSyntax"**" | "__"Preferred bold wrap syntax default: '**'
preferredItalicSyntax"*" | "_"Preferred italic wrap syntax default: '*'
preferredUnorderedListSyntax"-" | "*" | "+"Preferred unordered list prefix default: '-'
enableIndentExtensionbooleanWill handle tab and shift+tab keystrokes, on which will insert/remove indentation instead of the default behavior default:true
enableLinkPasteExtensionbooleanWill handle paste event, on which will wrap pasted with link/image markup if pasted is URL default:true
enablePrefixWrappingExtensionbooleanWill handle enter keystroke, on which will wrap current list sequence if needed default:true
enableProperLineRemoveBehaviorExtensionbooleanWill 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)
blockQuotesPlaceholderstringdefault: 'quote'
boldPlaceholderstringdefault: 'bold'
codeBlockPlaceholderstringdefault: 'code block'
codeInlinePlaceholderstringdefault: 'code'
headlinePlaceholderstring | (level: number) => stringdefault: (lvl) => 'headline ' + lvl
imageTextPlaceholderstringUsed inside default image markup ![<example>](...) default: 'example'
imageUrlPlaceholderstringUsed inside default image markup ![...](<image.png>) default: 'image.png'
italicPlaceholderstringdefault: 'italic'
linkTextPlaceholderstringUsed inside default link markup [<example>](...) default: 'example'
linkUrlPlaceholderstringUsed inside default image markup ![...](<url>) default: 'url'
orderedListPlaceholderstringdefault: 'ordered list'
strikeThroughPlaceholderstringdefault: 'strike through'
unorderedListPlaceholderstringdefault: 'unordered list'

TextareaMarkdownRef

ā„¹ļø Extends HTMLTextAreaElement instance

trigger: (command: string) => void;
1.0.2

2 years ago

1.0.5-rc.4

2 years ago

1.0.5-rc.2

2 years ago

1.0.5-rc.0

2 years ago

1.0.5-rc.1

2 years ago

1.0.4

2 years ago

1.0.3

2 years ago

1.0.1

2 years ago

1.0.0

2 years ago

1.0.0-rc.1

2 years ago

1.0.0-rc.2

2 years ago

1.0.0-rc.0

2 years ago

0.1.13

2 years ago

0.1.12

3 years ago

0.1.11

3 years ago

0.1.9

3 years ago

0.1.8

3 years ago

0.1.7

3 years ago

0.1.6

3 years ago

0.1.5

3 years ago

0.1.4

3 years ago

0.1.3

3 years ago

0.1.2

3 years ago

0.1.1

3 years ago

0.1.0

3 years ago