prosemirror-suggest
Primitives for building your prosemirror suggestion and autocomplete functionality.
The problem
You want to create a plugin for your prosemirror editor that responds to an activation character to create suggestions or options or actions for the user. Doing this from scratch can be difficult.
This solution
prosemirror-suggest
provides the suggestion primitives needed for building this functionality into your editor. You might be building user mentions, emoji search, an actions dropdown or anything that extracts a query from the editor after the activation character(s).
This implementation doesn't attempt to be magical. There's still a lot of work that goes into setting up your configuration. However, with this toolkit, you you will be building on a well-tested foundation with a structured API.
Installation
prosemirror-view
is a peer dependency of prosemirror-suggest
and needs to be installed as well.
yarn add prosemirror-suggest prosemirror-view
Getting Started
prosemirror-suggest
uses configuration objects called Suggester
's to define the behaviour of the suggestions you create. By calling the exported suggest
method with all required Suggester
's the functionality is added to the editor in one plugin.
In the following example we're creating an emoji suggestion plugin that responds to the colon character with a query and presents a list of matching emojis based on the query typed so far.
import { Suggester, suggest } from 'prosemirror-suggest';
const maxResults = 10;
let selectedIndex = 0;
let emojiList: string[] = [];
let showSuggestions = false;
const suggestEmojis: Suggester = {
noDecorations: true,
char: ':',
name: 'emoji-suggestion',
appendText: '',
keyBindings: {
ArrowUp: () => {
selectedIndex = rotateSelectionBackwards(selectedIndex, emojiList.length);
},
ArrowDown: () => {
selectedIndex = rotateSelectionForwards(selectedIndex, emojiList.length);
},
Enter: ({ command }) => {
if (showSuggestions) {
command(emojiList[selectedIndex]);
}
},
Esc: () => {
showSuggestions = false;
},
},
onChange: params => {
const query = params.query.full;
emojiList = sortEmojiMatches({ query, maxResults });
selectedIndex = 0;
showSuggestions = true;
},
onExit: () => {
showSuggestions = false;
emojiList = [];
selectedIndex = 0;
},
createCommand: ({ match, view }) => {
return (emoji, skinVariation) => {
if (!emoji) {
throw new Error('An emoji is required when calling the emoji suggestions command');
}
const tr = view.state.tr;
const { from, end: to } = match.range;
tr.insertText(emoji, from, to);
view.dispatch(tr);
};
},
};
const suggestionPlugin = suggest(suggestEmojis);
const state = EditorState.create({ schema, plugins: [suggestionPlugin] });
You can see this example brought to life in the remirror
codebase under the @remirror/extension-emoji
and the @remirror/extension-mention
packages.
The following examples are available for you to try out on the docs site
Search emoji on :
key
The emojis are inserted as plain text. It’s up to you whether you insert the emoji as a node, mark or plaintext.
Editable mention on @
key
Each mention is a mark that wraps the mentioned text with a spec property of inclusive: false
Core API
suggest
This creates a suggestion plugin with all the suggestions provided.
Signature:
suggest: <GSchema extends import("prosemirror-model").Schema<string, string> = any>(...suggesters: Suggester<import("@remirror/core-types").AnyFunction<void>>[]) => Plugin<SuggestState<any>, GSchema>
The priority of the suggestions is the order in which they are passed into this function.
const plugin = suggest(two, one, three);
In the above example two
will be checked first, then one
and then three
.
Only one suggestion can match at any given time. The order and specificity of the regex parameters help determines which suggestion will be active.
Suggester
This Suggester
interface defines all the options required to create a suggestion within your editor.
Signature:
export interface Suggester<GCommand extends AnyFunction<void> = AnyFunction<void>>
Properties
Property | Type | Description |
---|
appendText | string | Text to append after the mention has been added. |
char | string | The activation character(s) to match against. |
ignoredClassName | string | Set a class for the ignored suggestion decoration. |
ignoredTag | string | Set a tag for the ignored suggestion decoration. |
invalidPrefixCharacters | RegExp | string | A regex expression used to invalidate the text directly before the match. |
keyBindings | SuggestKeyBindingMap<GCommand> | An object that describes how certain key bindings should be handled. |
matchOffset | number | Sets the characters that need to be present after the initial character match before a match is triggered. |
name | string | A unique identifier for the suggester. |
noDecorations | boolean | When true, decorations are not created when this mention is being edited.. |
startOfLine | boolean | Whether to only match from the start of the line |
suggestClassName | string | Class name to use for the decoration (while the suggestion is still being written) |
suggestTag | string | Tag for the prosemirror decoration which wraps an active match. |
supportedCharacters | RegExp | string | A regex containing all supported characters when within a suggestion. |
validPrefixCharacters | RegExp | string | A regex expression used to validate the text directly before the match. |
Methods
Method | Description |
---|
createCommand(params) | Create the suggested actions which are made available to the onExit and ononChange handlers. |
onChange(params) | Called whenever a suggestion becomes active or changes in anyway. |
onCharacterEntry(params) | Called for each character entry and can be used to disable certain characters. |
onExit(params) | Called when a suggestion is exited with the pre-exit match value. |
The options are passed to the suggest method which uses them.
Package API
Enumerations
Enumeration | Description |
---|
ChangeReason | The potential reason for changes |
ExitReason | The potential reasons for an exit of a mention. |
Interfaces
Variables
Type Aliases