Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

prosemirror-suggest

Package Overview
Dependencies
Maintainers
1
Versions
273
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

prosemirror-suggest - npm Package Compare versions

Comparing version 1.0.0-next.0 to 1.0.0-next.1

dist/declarations/src/index.d.ts

13

CHANGELOG.md
# prosemirror-suggest
## 1.0.0-next.1
> 2020-07-05
### Patch Changes
- Fix missing dist files from previous publish.
- Updated dependencies [undefined]
- @remirror/core-constants@1.0.0-next.1
- @remirror/core-helpers@1.0.0-next.1
- @remirror/core-types@1.0.0-next.1
- @remirror/core-utils@1.0.0-next.1
## 1.0.0-next.0

@@ -4,0 +17,0 @@

12

dist/prosemirror-suggest.cjs.d.ts

@@ -1,11 +0,1 @@

// are you seeing an error that a default export doesn't exist but your source file has a default export?
// you should run `yarn` or `yarn preconstruct dev` if preconstruct dev isn't in your postinstall hook
// curious why you need to?
// this file exists so that you can import from the entrypoint normally
// except that it points to your source file and you don't need to run build constantly
// which means we need to re-export all of the modules from your source file
// and since export * doesn't include default exports, we need to read your source file
// to check for a default export and re-export it if it exists
// it's not ideal, but it works pretty well ¯\_(ツ)_/¯
export * from "../src/index";
export * from "./declarations/src/index";

@@ -1,19 +0,7 @@

"use strict";
// this file might look strange and you might be wondering what it's for
// it's lets you import your source files by importing this entrypoint
// as you would import it if it was built with preconstruct build
// this file is slightly different to some others though
// it has a require hook which compiles your code with Babel
// this means that you don't have to set up @babel/register or anything like that
// but you can still require this module and it'll be compiled
'use strict';
// this bit of code imports the require hook and registers it
let unregister = require("/Users/ifiokjr/Coding/kickjump/remirror/prs/node_modules/.pnpm/@preconstruct/hook@0.1.0/node_modules/@preconstruct/hook/dist/hook.cjs.js").___internalHook("/Users/ifiokjr/Coding/kickjump/remirror/prs");
// this re-exports the source file
module.exports = require("/Users/ifiokjr/Coding/kickjump/remirror/prs/packages/prosemirror-suggest/src/index.ts");
// this unregisters the require hook so that any modules required after this one
// aren't compiled with the require hook in case you have some other require hook
// or something that should be used on other modules
unregister();
if (process.env.NODE_ENV === "production") {
module.exports = require("./prosemirror-suggest.cjs.prod.js");
} else {
module.exports = require("./prosemirror-suggest.cjs.dev.js");
}

@@ -1,15 +0,1240 @@

// 👋 hey!!
// you might be reading this and seeing .esm in the filename
// and being confused why there is commonjs below this filename
// DON'T WORRY!
// this is intentional
// it's only commonjs with `preconstruct dev`
// when you run `preconstruct build`, it will be ESM
// why is it commonjs?
// we need to re-export every export from the source file
// but we can't do that with ESM without knowing what the exports are (because default exports aren't included in export/import *)
// and they could change after running `preconstruct dev` so we can't look at the file without forcing people to
// run preconstruct dev again which wouldn't be ideal
// this solution could change but for now, it's working
import { Plugin, PluginKey } from 'prosemirror-state';
import { isSelectionEmpty, hasTransactionChanged, getPluginState } from '@remirror/core-utils';
import _defineProperty from '@babel/runtime/helpers/esm/defineProperty';
import _classPrivateFieldSet from '@babel/runtime/helpers/esm/classPrivateFieldSet';
import _classPrivateFieldGet from '@babel/runtime/helpers/esm/classPrivateFieldGet';
import mergeDescriptors from 'merge-descriptors';
import { DecorationSet, Decoration } from 'prosemirror-view';
import { noop, bool, isString, isRegExp, object, entries, findMatches, isUndefined, isFunction } from '@remirror/core-helpers';
import _slicedToArray from '@babel/runtime/helpers/esm/slicedToArray';
import { keydownHandler } from 'prosemirror-keymap';
import { NULL_CHARACTER } from '@remirror/core-constants';
import escapeStringRegex from 'escape-string-regexp';
module.exports = require("/Users/ifiokjr/Coding/kickjump/remirror/prs/packages/prosemirror-suggest/src/index.ts")
var DEFAULT_SUGGEST_ACTIONS = {
command: noop,
create: noop,
remove: noop,
update: noop
};
var defaultHandler = () => false;
var DEFAULT_SUGGESTER = {
appendText: '',
createCommand: () => noop,
ignoredClassName: undefined,
ignoredTag: 'span',
invalidPrefixCharacters: undefined,
keyBindings: {},
matchOffset: 0,
noDecorations: false,
onChange: defaultHandler,
onCharacterEntry: defaultHandler,
onExit: defaultHandler,
startOfLine: false,
suggestClassName: 'suggest',
suggestTag: 'span',
supportedCharacters: /\w+/,
validPrefixCharacters: /^[\s\0]?$/
};
/**
* The potential reasons for an exit of a mention.
*/
var ExitReason;
/**
* The potential reason for changes
*/
(function (ExitReason) {
ExitReason["End"] = "exit-end";
ExitReason["Removed"] = "delete";
ExitReason["Split"] = "exit-split";
ExitReason["InvalidSplit"] = "invalid-exit-split";
ExitReason["MoveEnd"] = "move-end";
ExitReason["MoveStart"] = "move-start";
ExitReason["JumpForward"] = "jump-forward-exit";
ExitReason["JumpBackward"] = "jump-backward-exit";
ExitReason["SelectionOutside"] = "selection-outside";
})(ExitReason || (ExitReason = {}));
var ChangeReason;
(function (ChangeReason) {
ChangeReason["Start"] = "start";
ChangeReason["Text"] = "change-character";
ChangeReason["SelectionInside"] = "selection-inside";
ChangeReason["Move"] = "move";
ChangeReason["JumpBackward"] = "jump-backward-change";
ChangeReason["JumpForward"] = "jump-forward-change";
})(ChangeReason || (ChangeReason = {}));
/**
* Is this a change in the current suggestion (added or deleted characters)?
*/
var isChange = compare => bool(compare.prev && compare.next && compare.prev.queryText.full !== compare.next.queryText.full);
/**
* Has the cursor moved within the current suggestion (added or deleted
* characters)?
*/
var isMove = compare => bool(compare.prev && compare.next && compare.prev.range.to !== compare.next.range.to);
/**
* Are we entering a new suggestion?
*/
var isEntry = compare => bool(!compare.prev && compare.next);
/**
* Are we exiting a suggestion?
*/
var isExit = compare => bool(compare.prev && !compare.next);
/**
* Is this a jump from one suggestion to another?
*/
var isJump = compare => bool(compare.prev && compare.next && compare.prev.range.from !== compare.next.range.from);
/**
* Check that the passed in value is an ExitReason
*/
var isExitReason = value => isString(value) && Object.values(ExitReason).includes(value);
var isChangeReason = value => isString(value) && Object.values(ChangeReason).includes(value);
/**
* Checks that the reason passed is a split reason. This typically means that we
* should default to a partial update / creation of the mention.
*/
var isSplitReason = value => value === ExitReason.Split;
/**
* Checks that the reason was caused by a split at a point where there is no
* query.
*/
var isInvalidSplitReason = value => value === ExitReason.InvalidSplit;
/**
* Checks that the reason was caused by a deletion.
*/
var isRemovedReason = value => value === ExitReason.Removed;
/**
* Checks to see if this is a jump reason.
*/
var isJumpReason = map => map.exit ? [ExitReason.JumpBackward, ExitReason.JumpForward].includes(map.exit.reason) : map.change ? [ChangeReason.JumpBackward, ChangeReason.JumpForward].includes(map.change.reason) : false;
/**
* True when the match is currently active (i.e. it's query has a value)
*/
var isValidMatch = match => bool(match && match.queryText.full.length >= match.suggester.matchOffset);
/**
* True when the current selection is outside the match.
*/
var selectionOutsideMatch = (_ref) => {
var match = _ref.match,
selection = _ref.selection;
return match && (selection.from < match.range.from || selection.from > match.range.end);
};
var escapeChar = char => escapeStringRegex(char);
/**
* Convert a RegExp into a string
*
* @param regexOrString
*/
var regexToString = regexOrString => isRegExp(regexOrString) ? regexOrString.source : regexOrString;
/**
* Find regex prefix when depending on whether the mention only supports the
* start of a line or not
*
* @param onlyStartOfLine
*/
var getRegexPrefix = onlyStartOfLine => onlyStartOfLine ? '^' : '';
/**
* Get the supported characters regex string.
*/
var getRegexSupportedCharacters = (supportedCharacters, matchOffset) => "(?:".concat(regexToString(supportedCharacters), "){").concat(matchOffset, ",}");
/**
* Create a regex expression to evaluate matches directly from the suggester properties.
*/
var createRegexFromSuggestion = function createRegexFromSuggestion(_ref) {
var char = _ref.char,
matchOffset = _ref.matchOffset,
startOfLine = _ref.startOfLine,
supportedCharacters = _ref.supportedCharacters;
var flags = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'gm';
return new RegExp("".concat(getRegexPrefix(startOfLine)).concat(escapeChar(char)).concat(getRegexSupportedCharacters(supportedCharacters, matchOffset)), flags);
};
function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
/**
* Small utility method for creating a match with the reason property available.
*/
function createMatchWithReason(parameter) {
var match = parameter.match,
reason = parameter.reason;
return _objectSpread(_objectSpread({}, match), {}, {
reason
});
}
/**
* Checks to see if the text before the matching character is a valid prefix.
*
* @param prefix - the prefix to test
* @param params - an object with the regex testing values
*/
var isPrefixValid = (prefix, _ref) => {
var invalidPrefixCharacters = _ref.invalidPrefixCharacters,
validPrefixCharacters = _ref.validPrefixCharacters;
if (!isUndefined(invalidPrefixCharacters)) {
var regex = new RegExp(regexToString(invalidPrefixCharacters));
return !regex.test(prefix);
}
{
var _regex = new RegExp(regexToString(validPrefixCharacters));
return _regex.test(prefix);
}
};
/**
* Find the position of a mention for a given selection and character
*
* @param params
*/
var findPosition = (_ref2) => {
var text = _ref2.text,
regexp = _ref2.regexp,
$pos = _ref2.$pos,
char = _ref2.char,
suggester = _ref2.suggester;
var position;
var cursor = $pos.pos; // The current cursor position
var start = $pos.start(); // The starting position for matches
findMatches(text, regexp).forEach(match => {
// Check the character before the current match to ensure it is not one of
// the supported characters
var matchPrefix = match.input.slice(Math.max(0, match.index - 1), match.index);
if (isPrefixValid(matchPrefix, suggester)) {
var from = match.index + start; // The absolute position of the match wrapper node
var end = from + match[0].length; // The position where the match ends
var to = Math.min(end, cursor); // The cursor position (or end position whichever is greater)
var matchLength = to - from; // The length of the current match
// If the $position is located within the matched substring, return that
// range
if (from < cursor && end >= cursor) {
position = {
range: {
from,
end,
to
},
queryText: {
partial: match[0].slice(char.length, matchLength),
full: match[0].slice(char.length)
},
matchText: {
partial: match[0].slice(0, matchLength),
full: match[0]
},
suggester
};
}
}
});
return position;
};
/**
* Checks if any matches exist at the current selection for so that the
* suggesters be activated or deactivated.
*/
function findMatch(_ref3) {
var $pos = _ref3.$pos,
suggester = _ref3.suggester;
var char = suggester.char,
name = suggester.name,
startOfLine = suggester.startOfLine,
supportedCharacters = suggester.supportedCharacters,
matchOffset = suggester.matchOffset; // Create the regular expression to match the text against
var regexp = createRegexFromSuggestion({
char,
matchOffset,
startOfLine,
supportedCharacters
}); // All the text in the current node
var text = $pos.doc.textBetween($pos.before(), $pos.end(), NULL_CHARACTER, NULL_CHARACTER); // Find the position and return it
return findPosition({
suggester,
text,
regexp,
$pos,
char,
name
});
}
/**
* Checks the provided match and generates a new match. This is useful for
* determining the kind of change that has happened.
*
* If the match still exists and it is different then it's likely a split has
* occurred.
*/
var recheckMatch = (_ref4) => {
var state = _ref4.state,
match = _ref4.match;
try {
// Wrapped in try catch because it's possible for everything to be deleted
// and the doc.resolve will fail.
return findMatch({
$pos: state.doc.resolve(match.range.to),
suggester: match.suggester
});
} catch (_unused) {
return;
}
};
/**
* Check whether the insert action occurred at the end, in the middle or caused
* the suggestion to be invalid.
*
* Prev refers to the original previous and next refers to the updated version
* after the split
*/
var createInsertReason = (_ref5) => {
var prev = _ref5.prev,
next = _ref5.next,
state = _ref5.state;
// Has the text been removed? TODO how to tests for deletions mid document?
if (!next && prev.range.from >= state.doc.nodeSize) {
return {
exit: createMatchWithReason({
match: prev,
reason: ExitReason.Removed
})
};
} // Are we within an invalid split?
if (!next || !prev.queryText.partial) {
return {
exit: createMatchWithReason({
match: prev,
reason: ExitReason.InvalidSplit
})
};
} // Are we at the end position?
if (prev.range.end === next.range.to) {
return {
exit: createMatchWithReason({
match: next,
reason: ExitReason.End
})
};
} // Are we in the middle of the mention
if (prev.queryText.partial) {
return {
exit: createMatchWithReason({
match: next,
reason: ExitReason.Split
})
};
}
return {};
};
/**
* Find the reason for the Jump
*/
var findJumpReason = (_ref6) => {
var prev = _ref6.prev,
next = _ref6.next,
state = _ref6.state;
var value = object();
var updatedPrevious = recheckMatch({
state,
match: prev
});
var _ref7 = updatedPrevious && updatedPrevious.queryText.full !== prev.queryText.full // has query changed
? createInsertReason({
prev,
next: updatedPrevious,
state
}) : value,
exit = _ref7.exit;
var isJumpForward = prev.range.from < next.range.from;
if (isJumpForward) {
return {
exit: exit !== null && exit !== void 0 ? exit : createMatchWithReason({
match: prev,
reason: ExitReason.JumpForward
}),
change: createMatchWithReason({
match: next,
reason: ChangeReason.JumpForward
})
};
}
return {
exit: exit !== null && exit !== void 0 ? exit : createMatchWithReason({
match: prev,
reason: ExitReason.JumpBackward
}),
change: createMatchWithReason({
match: next,
reason: ChangeReason.JumpBackward
})
};
};
/**
* Find the reason for the exit.
*
* This provides some context and helps sets up a helper command with sane
* defaults.
*/
var findExitReason = (_ref8) => {
var match = _ref8.match,
state = _ref8.state,
$pos = _ref8.$pos;
var selection = state.selection;
var updatedPrevious = recheckMatch({
match,
state
}); // Exit created a split
if (!updatedPrevious || updatedPrevious.queryText.full !== match.queryText.full) {
return createInsertReason({
prev: match,
next: updatedPrevious,
state
});
} // Exit caused by a selection
if (!isSelectionEmpty(state) && (selection.from <= match.range.from || selection.to >= match.range.end)) {
return {
exit: createMatchWithReason({
match,
reason: ExitReason.SelectionOutside
})
};
} // Exit happened at the end of previous suggestion
if ($pos.pos > match.range.end) {
return {
exit: createMatchWithReason({
match,
reason: ExitReason.MoveEnd
})
};
} // Exit happened at the start of previous suggestion
if ($pos.pos <= match.range.from) {
return {
exit: createMatchWithReason({
match,
reason: ExitReason.MoveStart
})
};
}
return {};
};
/**
* Transforms the keybindings into an object that can be consumed by the
* prosemirror keydownHandler method.
*/
function transformKeyBindings(parameter) {
var bindings = parameter.bindings,
suggestionParameter = parameter.suggestionParameter;
var transformed = object();
var _iterator = _createForOfIteratorHelper(entries(bindings)),
_step;
try {
var _loop = function _loop() {
var _step$value = _slicedToArray(_step.value, 2),
key = _step$value[0],
binding = _step$value[1];
transformed[key] = () => bool(binding(suggestionParameter));
};
for (_iterator.s(); !(_step = _iterator.n()).done;) {
_loop();
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
return transformed;
}
/**
* Run the keyBindings when a key is pressed to perform actions.
*
* When return value is `true` no further actions should be taken for this key
* event. When `false` the event will be passed up the chain to the next key
* handler.
*
* This is useful for intercepting events.
*/
function runKeyBindings(bindings, suggestionParameter) {
return keydownHandler(transformKeyBindings({
bindings,
suggestionParameter
}))(suggestionParameter.view, suggestionParameter.event);
}
/**
* Creates an array of the actions taken based on the current prev and next
* state field
*/
var findReason = (_ref9) => {
var prev = _ref9.prev,
next = _ref9.next,
state = _ref9.state,
$pos = _ref9.$pos;
var value = object();
if (!prev && !next) {
return value;
}
var compare = {
prev,
next
}; // Check for a Jump
if (isJump(compare)) {
return findJumpReason({
prev: compare.prev,
next: compare.next,
state
});
} // Entered into a new suggestion
if (isEntry(compare)) {
return {
change: createMatchWithReason({
match: compare.next,
reason: ChangeReason.Start
})
};
} // Exited a suggestion
if (isExit(compare)) {
return findExitReason({
$pos,
match: compare.prev,
state
});
}
if (isChange(compare)) {
return {
change: createMatchWithReason({
match: compare.next,
reason: ChangeReason.Text
})
};
}
if (isMove(compare)) {
return {
change: createMatchWithReason({
match: compare.next,
reason: isSelectionEmpty(state) ? ChangeReason.Move : ChangeReason.SelectionInside
})
};
}
return value;
};
/**
* Find a match for the provided matchers
*/
function findFromSuggestions(_ref10) {
var suggesters = _ref10.suggesters,
$pos = _ref10.$pos;
// Find the first match and break when done
var _iterator2 = _createForOfIteratorHelper(suggesters),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var suggester = _step2.value;
try {
var match = findMatch({
suggester,
$pos
});
if (match) {
return match;
}
} catch (_unused2) {
console.warn('Error while finding match.');
}
}
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
return;
}
function ownKeys$1(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread$1(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys$1(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys$1(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
/**
* The suggestion state which manages the list of suggesters.
*/
var _ignoreNextExit = /*#__PURE__*/new WeakMap();
class SuggestState {
/**
* Create an instance of the SuggestState class.
*/
static create(suggesters) {
return new SuggestState(suggesters);
}
/**
* Returns the current active suggestion state field if one exists
*/
get match() {
return this.next ? this.next : this.prev && this.handlerMatches.exit ? this.prev : undefined;
}
/**
* Create the state for the `prosemirror-suggest` plugin.
*
* @remarks
*
* Each suggester must provide a name value which is globally unique since it
* acts as the identifier.
*
* It is possible to register multiple suggesters with identical `char`
* properties. The matched suggester is based on the specificity of the
* `regex` and the order in which they are passed in. Earlier suggesters are
* prioritized.
*/
constructor(suggesters) {
_ignoreNextExit.set(this, {
writable: true,
value: false
});
_defineProperty(this, "handlerMatches", object());
_defineProperty(this, "ignored", DecorationSet.empty);
_defineProperty(this, "removed", false);
_defineProperty(this, "setRemovedTrue", () => {
this.removed = true;
});
_defineProperty(this, "onViewUpdate", () => {
var match = this.match,
_this$handlerMatches = this.handlerMatches,
change = _this$handlerMatches.change,
exit = _this$handlerMatches.exit;
var shouldRunExit = () => {
if (_classPrivateFieldGet(this, _ignoreNextExit)) {
_classPrivateFieldSet(this, _ignoreNextExit, false);
return false;
}
return true;
}; // Cancel update when a suggestion isn't active
if (!change && !exit || !isValidMatch(match)) {
return;
} // When a jump happens run the action that involves the
// position that occurs later in the document. This is so that changes don't
// affect previous positions.
if (change && exit && isJumpReason({
change,
exit
})) {
var exitParameters = this.createReasonParameter(exit);
var changeParameters = this.createReasonParameter(change);
var movedForwards = exit.range.from < change.range.from;
if (movedForwards) {
change.suggester.onChange(changeParameters);
shouldRunExit() && exit.suggester.onExit(exitParameters);
} else {
shouldRunExit() && exit.suggester.onExit(exitParameters);
change.suggester.onChange(changeParameters);
}
this.removed = false;
return;
}
if (change) {
change.suggester.onChange(this.createReasonParameter(change));
}
if (exit && shouldRunExit()) {
exit.suggester.onExit(this.createReasonParameter(exit));
this.removed = false;
if (isInvalidSplitReason(exit.reason)) {
this.handlerMatches = object();
}
}
});
_defineProperty(this, "ignoreNextExit", () => {
_classPrivateFieldSet(this, _ignoreNextExit, true);
});
_defineProperty(this, "addIgnored", (_ref) => {
var from = _ref.from,
char = _ref.char,
name = _ref.name,
_ref$specific = _ref.specific,
specific = _ref$specific === void 0 ? false : _ref$specific;
var to = from + char.length;
var suggester = this.suggesters.find(value => value.name === name);
if (!suggester) {
throw new Error("No suggester exists for the name provided: ".concat(name));
}
var attributes = suggester.ignoredClassName ? {
class: suggester.ignoredClassName
} : {};
var decoration = Decoration.inline(from, to, _objectSpread$1({
nodeName: suggester.ignoredTag
}, attributes), {
char,
name,
specific
});
this.ignored = this.ignored.add(this.view.state.doc, [decoration]);
});
_defineProperty(this, "removeIgnored", (_ref2) => {
var from = _ref2.from,
char = _ref2.char,
name = _ref2.name;
var decorations = this.ignored.find(from, from + char.length);
var decoration = decorations[0];
if (!bool(decoration) || decoration.spec.name !== name) {
return;
}
this.ignored = this.ignored.remove([decoration]);
});
_defineProperty(this, "clearIgnored", name => {
if (name) {
var decorations = this.ignored.find();
var decorationsToClear = decorations.filter((_ref3) => {
var spec = _ref3.spec;
return spec.name === name;
});
this.ignored = this.ignored.remove(decorationsToClear);
} else {
this.ignored = DecorationSet.empty;
}
});
var names = [];
this.suggesters = suggesters.map(suggester => {
if (names.includes(suggester.name)) {
throw new Error("A suggester already exists with the name '".concat(suggester.name, "'. The name provided must be unique."));
}
var clone = _objectSpread$1(_objectSpread$1({}, DEFAULT_SUGGESTER), suggester); // Preserve any descriptors (getters and setters)
mergeDescriptors(clone, suggester);
names.push(suggester.name);
return clone;
});
}
/**
* Initialize the SuggestState with a view which is stored for use later.
*/
init(view) {
this.view = view;
return this;
}
/**
* Sets the removed property to be true. This is passed as a property to the
* `createCommand` option.
*/
/**
* The actions created by the extension.
*/
getCommand(match, reason) {
return match.suggester.createCommand({
match,
reason,
view: this.view,
setMarkRemoved: this.setRemovedTrue,
addIgnored: this.addIgnored,
clearIgnored: this.clearIgnored,
ignoreNextExit: this.ignoreNextExit
});
}
/**
* Create the props which should be passed into each action handler
*/
createParameter(match) {
return _objectSpread$1({
view: this.view,
addIgnored: this.addIgnored,
clearIgnored: this.clearIgnored,
ignoreNextExit: this.ignoreNextExit,
command: this.getCommand(match)
}, match);
}
/**
* Create the prop to be passed into the `onChange` or `onExit` handler.
*/
createReasonParameter(match) {
return _objectSpread$1(_objectSpread$1({}, this.createParameter(match)), {}, {
command: this.getCommand(match, match.reason)
}, match);
}
/**
* Manages the view updates.
*/
/**
* Update the current ignored decorations based on the latest changes to the
* prosemirror document.
*/
mapIgnoredDecorations(tr) {
// Map over and update the ignored decorations.
var ignored = this.ignored.map(tr.mapping, tr.doc);
var decorations = ignored.find(); // For suggesters with multiple characters it is possible for a `paste` or
// any edit action within the decoration to expand the ignored section. We
// check for that here and if the section size has changed it should be
// marked as invalid and removed from the ignored `DecorationSet`.
var invalid = decorations.filter((_ref4) => {
var from = _ref4.from,
to = _ref4.to,
spec = _ref4.spec;
if (to - from !== spec.char.length) {
return true;
}
return false;
});
this.ignored = ignored.remove(invalid);
}
shouldIgnoreMatch(_ref5) {
var range = _ref5.range,
name = _ref5.suggester.name;
var decorations = this.ignored.find();
return decorations.some((_ref6) => {
var spec = _ref6.spec,
from = _ref6.from;
if (from !== range.from) {
return false;
}
var shouldIgnore = spec.specific ? spec.name === name : true;
return shouldIgnore;
});
}
/**
* Reset the state.
*/
resetState() {
this.handlerMatches = object();
this.next = undefined;
this.removed = false;
}
/**
* Update the next state value.
*/
updateReasons(_ref7) {
var $pos = _ref7.$pos,
state = _ref7.state;
var match = findFromSuggestions({
suggesters: this.suggesters,
$pos
});
this.next = match && this.shouldIgnoreMatch(match) ? undefined : match; // Store the matches with reasons
this.handlerMatches = findReason({
next: this.next,
prev: this.prev,
state,
$pos
});
}
/**
* Used to handle the view property of the plugin spec.
*/
viewHandler() {
return {
update: this.onViewUpdate
};
}
toJSON() {
return this.match;
}
/**
* Applies updates to the state to be used within the plugins apply method.
*
* @param - params
*/
apply(_ref8) {
var tr = _ref8.tr,
newState = _ref8.newState;
var exit = this.handlerMatches.exit;
if (!hasTransactionChanged(tr) && !this.removed) {
return this;
}
this.mapIgnoredDecorations(tr); // If the previous run was an exit reset the suggestion matches
if (exit) {
this.resetState();
}
this.prev = this.next; // Match against the current selection position
this.updateReasons({
$pos: tr.selection.$from,
state: newState
});
return this;
}
/**
* Manages the keyDown event within the plugin props
*
* @param event
*/
handleKeyDown(event) {
var match = this.match;
if (!isValidMatch(match)) {
return false;
}
var keyBindings = match.suggester.keyBindings;
var parameter = _objectSpread$1({
event,
setMarkRemoved: this.setRemovedTrue
}, this.createParameter(match)); // TODO recalculating the keybindings on every update this is a performance bottleneck
return runKeyBindings(isFunction(keyBindings) ? keyBindings() : keyBindings, parameter);
}
/**
* Handle any key presses of non supported characters
*/
handleTextInput(_ref9) {
var text = _ref9.text,
from = _ref9.from,
to = _ref9.to;
var match = this.match;
if (!isValidMatch(match)) {
return false;
}
var onCharacterEntry = match.suggester.onCharacterEntry;
return onCharacterEntry(_objectSpread$1(_objectSpread$1({}, this.createParameter(match)), {}, {
from,
to,
text
}));
}
/**
* Handle the decorations which wrap the mention while it is active and not
* yet complete.
*/
decorations(state) {
var match = this.match;
if (!isValidMatch(match)) {
return this.ignored;
}
if (match.suggester.noDecorations) {
return this.ignored;
}
var range = match.range,
_match$suggester = match.suggester,
name = _match$suggester.name,
decorationsTag = _match$suggester.suggestTag,
suggestionClassName = _match$suggester.suggestClassName;
var from = range.from,
end = range.end;
return this.shouldIgnoreMatch(match) ? this.ignored : this.ignored.add(state.doc, [Decoration.inline(from, end, {
nodeName: decorationsTag,
class: name ? "".concat(suggestionClassName, " ").concat(suggestionClassName, "-").concat(name) : suggestionClassName
})]);
}
}
var suggestPluginKey = /*#__PURE__*/new PluginKey('suggest');
/**
* Get the state of the suggest plugin.
*
* @param state - the editor state.
*/
function getSuggestPluginState(state) {
return getPluginState(suggestPluginKey, state);
}
/**
* This creates a suggestion plugin with all the suggesters provided.
*
* @remarks
*
* 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.
*
* ```ts
* import { Suggestion, suggest } from 'prosemirror-suggest';
*
* const maxResults = 10;
* let selectedIndex = 0;
* let emojiList: string[] = [];
* let showSuggestions = false;
*
* const suggestEmojis: Suggestion = {
* // By default decorations are used to highlight the currently matched
* // suggestion in the dom.
* // In this example we don't need decorations (in fact they cause problems when the
* // emoji string replaces the query text in the dom).
* noDecorations: true,
* char: ':', // The character to match against
* name: 'emoji-suggestion', // a unique name
* appendText: '', // Text to append to the created match
*
* // Keybindings are similar to prosemirror keymaps with a few extra niceties.
* // The key identifier can also include modifiers (e.g.) `Cmd-Space: () => false`
* // Return true to prevent any further keyboard handlers from running.
* 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;
* },
*
* // Create a function that is passed into the change, exit and keybinding handlers.
* // This is useful when these handlers are called in a different part of the app.
* createCommand: ({ match, view }) => {
* return (emoji,skinVariation) => {
* if (!emoji) {
* throw new Error('An emoji is required when calling the emoji suggesters command');
* }
*
* const tr = view.state.tr; const { from, end: to } = match.range;
* tr.insertText(emoji, from, to); view.dispatch(tr);
* };
* },
* };
*
* // Create the plugin with the above configuration. It also supports multiple plugins being added.
* const suggestionPlugin = suggest(suggestEmojis);
*
* // Include the plugin in the created editor state.
* const state = EditorState.create({schema,
* plugins: [suggestionPlugin],
* });
* ```
*
* The priority of the suggesters is the order in which they are passed into
* this function.
*
* - `const plugin = suggest(two, one, three)` - Here `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.
*
* @param suggesters - a list of suggesters in the order they should be
* evaluated.
*/
function suggest() {
for (var _len = arguments.length, suggesters = new Array(_len), _key = 0; _key < _len; _key++) {
suggesters[_key] = arguments[_key];
}
var pluginState = SuggestState.create(suggesters);
return new Plugin({
key: suggestPluginKey,
// Handle the plugin view
view: _view => {
return pluginState.init(_view).viewHandler();
},
state: {
// Initialize the state
init: () => {
return pluginState;
},
// Apply changes to the state
apply: (tr, _, oldState, newState) => {
return pluginState.apply({
tr,
oldState,
newState
});
}
},
props: {
// Call the keydown hook if suggestion is active.
handleKeyDown: (_, event) => {
return pluginState.handleKeyDown(event);
},
// Defer to the pluginState handler
handleTextInput: (_, from, to, text) => {
return pluginState.handleTextInput({
text,
from,
to
});
},
// Sets up a decoration (styling options) on the currently active
// decoration
decorations: state => {
return pluginState.decorations(state);
}
}
});
}
export { ChangeReason, DEFAULT_SUGGESTER, DEFAULT_SUGGEST_ACTIONS, ExitReason, createRegexFromSuggestion, escapeChar, getRegexPrefix, getSuggestPluginState, isChange, isChangeReason, isEntry, isExit, isExitReason, isInvalidSplitReason, isJump, isJumpReason, isMove, isRemovedReason, isSplitReason, isValidMatch, regexToString, selectionOutsideMatch, suggest };
{
"name": "prosemirror-suggest",
"version": "1.0.0-next.0",
"version": "1.0.0-next.1",
"description": "Primitives for building your prosemirror suggestion and autocomplete functionality",

@@ -20,6 +20,6 @@ "homepage": "https://github.com/remirror/remirror/tree/next/packages/prosemirror-suggest",

"@babel/runtime": "^7.10.4",
"@remirror/core-constants": "^1.0.0-next.0",
"@remirror/core-helpers": "^1.0.0-next.0",
"@remirror/core-types": "^1.0.0-next.0",
"@remirror/core-utils": "^1.0.0-next.0",
"@remirror/core-constants": "^1.0.0-next.1",
"@remirror/core-helpers": "^1.0.0-next.1",
"@remirror/core-types": "^1.0.0-next.1",
"@remirror/core-utils": "^1.0.0-next.1",
"@types/merge-descriptors": "1.0.1",

@@ -26,0 +26,0 @@ "@types/prosemirror-keymap": "^1.0.2",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc