Socket
Socket
Sign inDemoInstall

@ckeditor/ckeditor5-mention

Package Overview
Dependencies
Maintainers
1
Versions
630
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ckeditor/ckeditor5-mention - npm Package Compare versions

Comparing version 16.0.0 to 17.0.0

7

CHANGELOG.md
Changelog
=========
## [17.0.0](https://github.com/ckeditor/ckeditor5-mention/compare/v16.0.0...v17.0.0) (2020-02-19)
### Features
* Implemented debounced mechanism for requesting a mention feed. Closes [ckeditor/ckeditor5#4619](https://github.com/ckeditor/ckeditor5/issues/4619). ([f50db9c](https://github.com/ckeditor/ckeditor5-mention/commit/f50db9c))
## [16.0.0](https://github.com/ckeditor/ckeditor5-mention/compare/v15.0.0...v16.0.0) (2019-12-04)

@@ -5,0 +12,0 @@

2

LICENSE.md

@@ -5,3 +5,3 @@ Software License Agreement

**CKEditor 5 Mention Feature** – https://github.com/ckeditor/ckeditor5-mention <br>
Copyright (c) 2003-2019, [CKSource](http://cksource.com) Frederico Knabben. All rights reserved.
Copyright (c) 2003-2020, [CKSource](http://cksource.com) Frederico Knabben. All rights reserved.

@@ -8,0 +8,0 @@ Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html).

{
"name": "@ckeditor/ckeditor5-mention",
"version": "16.0.0",
"version": "17.0.0",
"description": "Mention feature for CKEditor 5.",

@@ -13,19 +13,20 @@ "keywords": [

"dependencies": {
"@ckeditor/ckeditor5-core": "^16.0.0",
"@ckeditor/ckeditor5-ui": "^16.0.0",
"@ckeditor/ckeditor5-typing": "^16.0.0",
"@ckeditor/ckeditor5-utils": "^16.0.0"
"@ckeditor/ckeditor5-core": "^17.0.0",
"@ckeditor/ckeditor5-ui": "^17.0.0",
"@ckeditor/ckeditor5-typing": "^17.0.0",
"@ckeditor/ckeditor5-utils": "^17.0.0",
"lodash-es": "^4.17.10"
},
"devDependencies": {
"@ckeditor/ckeditor5-basic-styles": "^16.0.0",
"@ckeditor/ckeditor5-block-quote": "^16.0.0",
"@ckeditor/ckeditor5-clipboard": "^16.0.0",
"@ckeditor/ckeditor5-editor-classic": "^16.0.0",
"@ckeditor/ckeditor5-engine": "^16.0.0",
"@ckeditor/ckeditor5-font": "^16.0.0",
"@ckeditor/ckeditor5-link": "^16.0.0",
"@ckeditor/ckeditor5-paragraph": "^16.0.0",
"@ckeditor/ckeditor5-table": "^16.0.0",
"@ckeditor/ckeditor5-undo": "^16.0.0",
"@ckeditor/ckeditor5-widget": "^16.0.0",
"@ckeditor/ckeditor5-basic-styles": "^17.0.0",
"@ckeditor/ckeditor5-block-quote": "^17.0.0",
"@ckeditor/ckeditor5-clipboard": "^17.0.0",
"@ckeditor/ckeditor5-editor-classic": "^17.0.0",
"@ckeditor/ckeditor5-engine": "^17.0.0",
"@ckeditor/ckeditor5-font": "^17.0.0",
"@ckeditor/ckeditor5-link": "^17.0.0",
"@ckeditor/ckeditor5-paragraph": "^17.0.0",
"@ckeditor/ckeditor5-table": "^17.0.0",
"@ckeditor/ckeditor5-undo": "^17.0.0",
"@ckeditor/ckeditor5-widget": "^17.0.0",
"eslint": "^5.5.0",

@@ -35,2 +36,3 @@ "eslint-config-ckeditor5": "^2.0.0",

"lint-staged": "^7.0.0",
"lodash": "^4.17.11",
"stylelint": "^11.1.1",

@@ -37,0 +39,0 @@ "stylelint-config-ckeditor5": "^1.0.0"

/**
* @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
* @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license

@@ -4,0 +4,0 @@ */

/**
* @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
* @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license

@@ -4,0 +4,0 @@ */

/**
* @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
* @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license

@@ -84,3 +84,3 @@ */

* @param {String|Object} [data] Mention data to be extended.
* @return {module:mention/mention~MentionAttribute}
* @returns {module:mention/mention~MentionAttribute}
*/

@@ -87,0 +87,0 @@ export function _toMentionAttribute( viewElementOrMention, data ) {

/**
* @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
* @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license

@@ -10,2 +10,4 @@ */

/* global console */
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';

@@ -18,4 +20,5 @@ import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';

import Rect from '@ckeditor/ckeditor5-utils/src/dom/rect';
import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
import CKEditorError, { attachLinkToDocumentation } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
import ContextualBalloon from '@ckeditor/ckeditor5-ui/src/panel/balloon/contextualballoon';
import { debounce } from 'lodash-es';

@@ -30,2 +33,12 @@ import TextWatcher from '@ckeditor/ckeditor5-typing/src/textwatcher';

// The key codes that mention UI handles when it is open.
const handledKeyCodes = [
keyCodes.arrowup,
keyCodes.arrowdown,
keyCodes.enter,
keyCodes.tab,
keyCodes.space,
keyCodes.esc
];
/**

@@ -47,2 +60,9 @@ * The mention UI feature.

*/
static get requires() {
return [ ContextualBalloon ];
}
/**
* @inheritDoc
*/
constructor( editor ) {

@@ -67,2 +87,12 @@ super( editor );

/**
* Debounced feed requester. It uses `lodash#debounce` method to delay function call.
*
* @private
* @param {String} marker
* @param {String} feedText
* @method
*/
this._requestFeedDebounced = debounce( this._requestFeed, 100 );
editor.config.define( 'mention', { feeds: [] } );

@@ -124,3 +154,3 @@ }

if ( !marker || marker.length != 1 ) {
if ( !isValidMentionMarker( marker ) ) {
/**

@@ -152,2 +182,5 @@ * The marker must be a single character.

}
this.on( 'requestFeed:response', ( evt, data ) => this._handleFeedResponse( data ) );
this.on( 'requestFeed:error', () => this._hideUIAndRemoveMarker() );
}

@@ -166,9 +199,2 @@

/**
* @inheritDoc
*/
static get requires() {
return [ ContextualBalloon ];
}
/**
* Returns true when {@link #_mentionsView} is in the {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon} and it is

@@ -263,13 +289,76 @@ * currently visible.

/**
* Returns a promise that resolves with autocomplete items for a given text.
* Requests a feed from a configured callbacks.
*
* @private
* @fires module:mention/mentionui~MentionUI#event:requestFeed:response
* @fires module:mention/mentionui~MentionUI#event:requestFeed:discarded
* @fires module:mention/mentionui~MentionUI#event:requestFeed:error
* @param {String} marker
* @param {String} feedText
* @return {Promise<module:mention/mention~MentionFeedItem>}
* @private
*/
_getFeed( marker, feedText ) {
_requestFeed( marker, feedText ) {
// Store the last requested feed - it is used to discard any out-of order requests.
this._lastRequested = feedText;
const { feedCallback } = this._mentionsConfigurations.get( marker );
const feedResponse = feedCallback( feedText );
return Promise.resolve().then( () => feedCallback( feedText ) );
const isAsynchronous = feedResponse instanceof Promise;
// For synchronous feeds (e.g. callbacks, arrays) fire the response event immediately.
if ( !isAsynchronous ) {
/**
* Fired whenever requested feed has a response.
*
* @event requestFeed:response
* @param {Object} data Event data.
* @param {Array.<module:mention/mention~MentionFeedItem>} data.feed Autocomplete items.
* @param {String} data.marker The character which triggers autocompletion for mention.
* @param {String} data.feedText The text for which feed items were requested.
*/
this.fire( 'requestFeed:response', { feed: feedResponse, marker, feedText } );
return;
}
// Handle the asynchronous responses.
feedResponse
.then( response => {
// Check the feed text of this response with the last requested one so either:
if ( this._lastRequested == feedText ) {
// It is the same and fire the response event.
this.fire( 'requestFeed:response', { feed: response, marker, feedText } );
} else {
// It is different - most probably out-of-order one, so fire the discarded event.
/**
* Fired whenever the requested feed was discarded. This happens when the response was delayed and
* other feed was already requested.
*
* @event requestFeed:discarded
* @param {Object} data Event data.
* @param {Array.<module:mention/mention~MentionFeedItem>} data.feed Autocomplete items.
* @param {String} data.marker The character which triggers autocompletion for mention.
* @param {String} data.feedText The text for which feed items were requested.
*/
this.fire( 'requestFeed:discarded', { feed: response, marker, feedText } );
}
} )
.catch( error => {
/**
* Fired whenever the requested {@link module:mention/mention~MentionFeed#feed} promise fails with error.
*
* @event requestFeed:error
* @param {Object} data Event data.
* @param {Error} data.error The error that was caught.
*/
this.fire( 'requestFeed:error', { error } );
/**
* The callback used for obtaining mention autocomplete feed thrown and error and the mention UI was hidden or
* not displayed at all.
*
* @error mention-feed-callback-error
*/
console.warn( attachLinkToDocumentation( 'mention-feed-callback-error: Could not obtain mention autocomplete feed.' ) );
} );
}

@@ -294,10 +383,3 @@

// The text watcher listens only to changed range in selection - so the selection attributes are not yet available
// and you cannot use selection.hasAttribute( 'mention' ) just yet.
// See https://github.com/ckeditor/ckeditor5-engine/issues/1723.
const hasMention = focus.textNode && focus.textNode.hasAttribute( 'mention' );
const nodeBefore = focus.nodeBefore;
if ( hasMention || nodeBefore && nodeBefore.is( 'text' ) && nodeBefore.hasAttribute( 'mention' ) ) {
if ( hasExistingMention( focus ) ) {
this._hideUIAndRemoveMarker();

@@ -308,3 +390,3 @@

const feedText = getFeedText( marker, data.text );
const feedText = requestFeedText( marker, data.text );
const matchedTextLength = marker.length + feedText.length;

@@ -318,30 +400,16 @@

let mentionMarker;
if ( checkIfStillInCompletionMode( editor ) ) {
const mentionMarker = editor.model.markers.get( 'mention' );
if ( editor.model.markers.has( 'mention' ) ) {
mentionMarker = editor.model.markers.get( 'mention' );
// Update the marker - user might've moved the selection to other mention trigger.
editor.model.change( writer => {
writer.updateMarker( mentionMarker, { range: markerRange } );
} );
} else {
mentionMarker = editor.model.change( writer => writer.addMarker( 'mention', {
range: markerRange,
usingOperation: false,
affectsData: false
} ) );
editor.model.change( writer => {
writer.addMarker( 'mention', { range: markerRange, usingOperation: false, affectsData: false } );
} );
}
this._getFeed( marker, feedText )
.then( feed => {
this._items.clear();
for ( const feedItem of feed ) {
const item = typeof feedItem != 'object' ? { id: feedItem, text: feedItem } : feedItem;
this._items.add( { item, marker } );
}
if ( this._items.length ) {
this._showUI( mentionMarker );
} else {
this._hideUIAndRemoveMarker();
}
} );
this._requestFeedDebounced( marker, feedText );
} );

@@ -357,2 +425,35 @@

/**
* Handles the feed response event data.
*
* @param data
* @private
*/
_handleFeedResponse( data ) {
const { feed, marker } = data;
// If the marker is not in the document happens when the selection had changed and the 'mention' marker was removed.
if ( !checkIfStillInCompletionMode( this.editor ) ) {
return;
}
// Reset the view.
this._items.clear();
for ( const feedItem of feed ) {
const item = typeof feedItem != 'object' ? { id: feedItem, text: feedItem } : feedItem;
this._items.add( { item, marker } );
}
const mentionMarker = this.editor.model.markers.get( 'mention' );
if ( this._items.length ) {
this._showOrUpdateUI( mentionMarker );
} else {
// Do not show empty mention UI.
this._hideUIAndRemoveMarker();
}
}
/**
* Shows the mentions balloon. If the panel is already visible, it will reposition it.

@@ -362,3 +463,3 @@ *

*/
_showUI( markerMarker ) {
_showOrUpdateUI( markerMarker ) {
if ( this._isUIVisible ) {

@@ -377,3 +478,2 @@ // Update balloon position as the mention list view may change its size.

this._mentionsView.position = this._balloon.view.position;
this._mentionsView.selectFirst();

@@ -393,3 +493,3 @@ }

if ( this.editor.model.markers.has( 'mention' ) ) {
if ( checkIfStillInCompletionMode( this.editor ) ) {
this.editor.model.change( writer => writer.removeMarker( 'mention' ) );

@@ -451,3 +551,3 @@ }

const editor = this.editor;
const editing = this.editor.editing;
const editing = editor.editing;
const domConverter = editing.view.domConverter;

@@ -586,3 +686,3 @@ const mapper = editing.mapper;

// @returns {Function}
function getFeedText( marker, text ) {
function requestFeedText( marker, text ) {
const regExp = createRegExp( marker, 0 );

@@ -610,3 +710,3 @@

return Promise.resolve( filteredItems );
return filteredItems;
};

@@ -620,12 +720,33 @@ }

function isHandledKey( keyCode ) {
const handledKeyCodes = [
keyCodes.arrowup,
keyCodes.arrowdown,
keyCodes.enter,
keyCodes.tab,
keyCodes.space,
keyCodes.esc
];
return handledKeyCodes.includes( keyCode );
}
// Checks if position in inside or right after a text with a mention.
//
// @param {module:engine/model/position~Position} position.
// @returns {Boolean}
function hasExistingMention( position ) {
// The text watcher listens only to changed range in selection - so the selection attributes are not yet available
// and you cannot use selection.hasAttribute( 'mention' ) just yet.
// See https://github.com/ckeditor/ckeditor5-engine/issues/1723.
const hasMention = position.textNode && position.textNode.hasAttribute( 'mention' );
const nodeBefore = position.nodeBefore;
return hasMention || nodeBefore && nodeBefore.is( 'text' ) && nodeBefore.hasAttribute( 'mention' );
}
// Checks if string is a valid mention marker.
//
// @param {String} marker
// @returns {Boolean}
function isValidMentionMarker( marker ) {
return marker && marker.length == 1;
}
// Checks the mention plugins is in completion mode (e.g. when typing is after a valid mention string like @foo).
//
// @returns {Boolean}
function checkIfStillInCompletionMode( editor ) {
return editor.model.markers.has( 'mention' );
}
/**
* @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
* @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license

@@ -4,0 +4,0 @@ */

/**
* @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
* @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license

@@ -4,0 +4,0 @@ */

/**
* @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
* @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license

@@ -4,0 +4,0 @@ */

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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