@ckeditor/ckeditor5-mention 30.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +17 -0
- package/README.md +20 -0
- package/build/mention.js +5 -0
- package/ckeditor5-metadata.json +18 -0
- package/package.json +63 -0
- package/src/index.js +12 -0
- package/src/mention.js +260 -0
- package/src/mentioncommand.js +150 -0
- package/src/mentionediting.js +291 -0
- package/src/mentionui.js +743 -0
- package/src/ui/domwrapperview.js +78 -0
- package/src/ui/mentionlistitemview.js +24 -0
- package/src/ui/mentionsview.js +123 -0
- package/theme/mention.css +4 -0
- package/theme/mentionui.css +27 -0
package/LICENSE.md
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
Software License Agreement
|
2
|
+
==========================
|
3
|
+
|
4
|
+
**CKEditor 5 mention feature** – https://github.com/ckeditor/ckeditor5-mention <br>
|
5
|
+
Copyright (c) 2003-2021, [CKSource](http://cksource.com) Frederico Knabben. All rights reserved.
|
6
|
+
|
7
|
+
Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html).
|
8
|
+
|
9
|
+
Sources of Intellectual Property Included in CKEditor
|
10
|
+
-----------------------------------------------------
|
11
|
+
|
12
|
+
Where not otherwise indicated, all CKEditor content is authored by CKSource engineers and consists of CKSource-owned intellectual property. In some specific instances, CKEditor will incorporate work done by developers outside of CKSource with their express permission.
|
13
|
+
|
14
|
+
Trademarks
|
15
|
+
----------
|
16
|
+
|
17
|
+
**CKEditor** is a trademark of [CKSource](http://cksource.com) Frederico Knabben. All other brand and product names are trademarks, registered trademarks or service marks of their respective holders.
|
package/README.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
CKEditor 5 mention feature
|
2
|
+
===========================
|
3
|
+
|
4
|
+
[](https://www.npmjs.com/package/@ckeditor/ckeditor5-mention)
|
5
|
+
[](https://coveralls.io/github/ckeditor/ckeditor5?branch=master)
|
6
|
+
[](https://travis-ci.com/ckeditor/ckeditor5)
|
7
|
+
|
8
|
+
This package implements mention support for CKEditor 5 and brings smart autocompletion based on user input.
|
9
|
+
|
10
|
+
## Demo
|
11
|
+
|
12
|
+
Check out the demo in the [mentions (autocomplete) feature guide](https://ckeditor.com/docs/ckeditor5/latest/features/mentions.html#demo).
|
13
|
+
|
14
|
+
## Documentation
|
15
|
+
|
16
|
+
See the [`@ckeditor/ckeditor5-mention` package](https://ckeditor.com/docs/ckeditor5/latest/api/mention.html) page in [CKEditor 5 documentation](https://ckeditor.com/docs/ckeditor5/latest/).
|
17
|
+
|
18
|
+
## License
|
19
|
+
|
20
|
+
Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html). For full details about the license, please check the `LICENSE.md` file or [https://ckeditor.com/legal/ckeditor-oss-license](https://ckeditor.com/legal/ckeditor-oss-license).
|
package/build/mention.js
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
/*!
|
2
|
+
* @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
|
3
|
+
* For licensing, see LICENSE.md.
|
4
|
+
*/
|
5
|
+
window.CKEditor5=window.CKEditor5||{},window.CKEditor5.mention=function(e){var t={};function n(i){if(t[i])return t[i].exports;var o=t[i]={i:i,l:!1,exports:{}};return e[i].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,i){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:i})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(i,o,function(t){return e[t]}.bind(null,o));return i},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=12)}([function(e,t,n){e.exports=n(3)("./src/utils.js")},function(e,t,n){e.exports=n(3)("./src/ui.js")},function(e,t,n){e.exports=n(3)("./src/core.js")},function(e,t){e.exports=CKEditor5.dll},function(e,t,n){"use strict";var i,o=function(){return void 0===i&&(i=Boolean(window&&document&&document.all&&!window.atob)),i},r=function(){var e={};return function(t){if(void 0===e[t]){var n=document.querySelector(t);if(window.HTMLIFrameElement&&n instanceof window.HTMLIFrameElement)try{n=n.contentDocument.head}catch(e){n=null}e[t]=n}return e[t]}}(),s=[];function c(e){for(var t=-1,n=0;n<s.length;n++)if(s[n].identifier===e){t=n;break}return t}function a(e,t){for(var n={},i=[],o=0;o<e.length;o++){var r=e[o],a=t.base?r[0]+t.base:r[0],d=n[a]||0,u="".concat(a," ").concat(d);n[a]=d+1;var l=c(u),m={css:r[1],media:r[2],sourceMap:r[3]};-1!==l?(s[l].references++,s[l].updater(m)):s.push({identifier:u,updater:p(m,t),references:1}),i.push(u)}return i}function d(e){var t=document.createElement("style"),i=e.attributes||{};if(void 0===i.nonce){var o=n.nc;o&&(i.nonce=o)}if(Object.keys(i).forEach((function(e){t.setAttribute(e,i[e])})),"function"==typeof e.insert)e.insert(t);else{var s=r(e.insert||"head");if(!s)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");s.appendChild(t)}return t}var u,l=(u=[],function(e,t){return u[e]=t,u.filter(Boolean).join("\n")});function m(e,t,n,i){var o=n?"":i.media?"@media ".concat(i.media," {").concat(i.css,"}"):i.css;if(e.styleSheet)e.styleSheet.cssText=l(t,o);else{var r=document.createTextNode(o),s=e.childNodes;s[t]&&e.removeChild(s[t]),s.length?e.insertBefore(r,s[t]):e.appendChild(r)}}function f(e,t,n){var i=n.css,o=n.media,r=n.sourceMap;if(o?e.setAttribute("media",o):e.removeAttribute("media"),r&&"undefined"!=typeof btoa&&(i+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(r))))," */")),e.styleSheet)e.styleSheet.cssText=i;else{for(;e.firstChild;)e.removeChild(e.firstChild);e.appendChild(document.createTextNode(i))}}var h=null,g=0;function p(e,t){var n,i,o;if(t.singleton){var r=g++;n=h||(h=d(t)),i=m.bind(null,n,r,!1),o=m.bind(null,n,r,!0)}else n=d(t),i=f.bind(null,n,t),o=function(){!function(e){if(null===e.parentNode)return!1;e.parentNode.removeChild(e)}(n)};return i(e),function(t){if(t){if(t.css===e.css&&t.media===e.media&&t.sourceMap===e.sourceMap)return;i(e=t)}else o()}}e.exports=function(e,t){(t=t||{}).singleton||"boolean"==typeof t.singleton||(t.singleton=o());var n=a(e=e||[],t);return function(e){if(e=e||[],"[object Array]"===Object.prototype.toString.call(e)){for(var i=0;i<n.length;i++){var o=c(n[i]);s[o].references--}for(var r=a(e,t),d=0;d<n.length;d++){var u=c(n[d]);0===s[u].references&&(s[u].updater(),s.splice(u,1))}n=r}}}},function(e,t,n){e.exports=n(3)("./src/typing.js")},function(e,t,n){"use strict";(function(e){var n="object"==typeof e&&e&&e.Object===Object&&e;t.a=n}).call(this,n(9))},function(e,t,n){var i=n(4),o=n(8);"string"==typeof(o=o.__esModule?o.default:o)&&(o=[[e.i,o,""]]);var r={injectType:"singletonStyleTag",attributes:{"data-cke":!0},insert:"head",singleton:!0};i(o,r);e.exports=o.locals||{}},function(e,t){e.exports=":root{--ck-mention-list-max-height:300px}.ck.ck-mentions{max-height:var(--ck-mention-list-max-height);overflow-y:auto;overflow-x:hidden;overscroll-behavior:contain}.ck.ck-mentions>.ck-list__item{overflow:hidden;flex-shrink:0}"},function(e,t){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(e){"object"==typeof window&&(n=window)}e.exports=n},function(e,t,n){var i=n(4),o=n(11);"string"==typeof(o=o.__esModule?o.default:o)&&(o=[[e.i,o,""]]);var r={injectType:"singletonStyleTag",attributes:{"data-cke":!0},insert:"head",singleton:!0};i(o,r);e.exports=o.locals||{}},function(e,t){e.exports=":root{--ck-color-mention-background:rgba(153,0,48,0.1);--ck-color-mention-text:#990030}.ck-content .mention{background:var(--ck-color-mention-background);color:var(--ck-color-mention-text)}"},function(e,t,n){"use strict";n.r(t),n.d(t,"Mention",(function(){return te})),n.d(t,"MentionEditing",(function(){return s})),n.d(t,"MentionUI",(function(){return G}));var i=n(2),o=n(0);class r extends i.Command{refresh(){const e=this.editor.model,t=e.document;this.isEnabled=e.schema.checkAttributeInSelection(t.selection,"mention")}execute(e){const t=this.editor.model,n=t.document.selection,i="string"==typeof e.mention?{id:e.mention}:e.mention,r=i.id,s=e.range||n.getFirstRange(),a=e.text||r,d=c({_text:a,id:r},i);if(1!=e.marker.length)throw new o.CKEditorError("mentioncommand-incorrect-marker",this);if(r.charAt(0)!=e.marker)throw new o.CKEditorError("mentioncommand-incorrect-id",this);t.change(e=>{const i=Object(o.toMap)(n.getAttributes()),r=new Map(i.entries());r.set("mention",d),t.insertContent(e.createText(a,r),s),t.insertContent(e.createText(" ",i),s.start.getShiftedBy(a.length))})}}class s extends i.Plugin{static get pluginName(){return"MentionEditing"}init(){const e=this.editor,t=e.model,n=t.document;t.schema.extend("$text",{allowAttributes:"mention"}),e.conversion.for("upcast").elementToAttribute({view:{name:"span",key:"data-mention",classes:"mention"},model:{key:"mention",value:e=>a(e)}}),e.conversion.for("downcast").attributeToElement({model:"mention",view:u}),e.conversion.for("downcast").add(d),n.registerPostFixer(e=>function(e,t,n){const i=t.differ.getChanges();let o=!1;for(const t of i){const i=t.position;if("$text"==t.name){const t=i.textNode&&i.textNode.nextSibling;o=m(i.textNode,e)||o,o=m(t,e)||o,o=m(i.nodeBefore,e)||o,o=m(i.nodeAfter,e)||o}if("$text"!=t.name&&"insert"==t.type){const t=i.nodeAfter;for(const n of e.createRangeIn(t).getItems())o=m(n,e)||o}if("insert"==t.type&&n.isInline(t.name)){const t=i.nodeAfter&&i.nodeAfter.nextSibling;o=m(i.nodeBefore,e)||o,o=m(t,e)||o}}return o}(e,n,t.schema)),n.registerPostFixer(e=>function(e,t){const n=t.differ.getChanges();let i=!1;for(const t of n)if("attribute"===t.type&&"mention"!=t.attributeKey){const n=t.range.start.nodeBefore,o=t.range.end.nodeAfter;for(const r of[n,o])l(r)&&r.getAttribute(t.attributeKey)!=t.attributeNewValue&&(e.setAttribute(t.attributeKey,t.attributeNewValue,r),i=!0)}return i}(e,n)),n.registerPostFixer(e=>function(e,t){const n=t.selection,i=n.focus;if(n.isCollapsed&&n.hasAttribute("mention")&&function(e){const t=e.isAtStart;return e.nodeBefore&&e.nodeBefore.is("$text")||t}(i))return e.removeSelectionAttribute("mention"),!0}(e,n)),e.commands.add("mention",new r(e))}}function c(e,t){return Object.assign({uid:Object(o.uid)()},e,t||{})}function a(e,t){const n=e.getAttribute("data-mention"),i=e.getChild(0);if(!i)return;return c({id:n,_text:i.data},t)}function d(e){e.on("attribute:mention",(e,t,n)=>{const i=t.attributeNewValue;if(!t.item.is("$textProxy")||!i)return;const o=t.range.start;(o.textNode||o.nodeAfter).data!=i._text&&n.consumable.consume(t.item,e.name)},{priority:"highest"})}function u(e,{writer:t}){if(!e)return;const n={class:"mention","data-mention":e.id},i={id:e.uid,priority:20};return t.createAttributeElement("span",n,i)}function l(e){if(!e||!e.is("$text")&&!e.is("$textProxy")||!e.hasAttribute("mention"))return!1;return e.data!=e.getAttribute("mention")._text}function m(e,t){return!!l(e)&&(t.removeAttribute("mention",e),!0)}var f=n(1),h=n(5);var g=function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)},p=n(6),v="object"==typeof self&&self&&self.Object===Object&&self,b=p.a||v||Function("return this")(),w=function(){return b.Date.now()},x=/\s/;var y=function(e){for(var t=e.length;t--&&x.test(e.charAt(t)););return t},_=/^\s+/;var k=function(e){return e?e.slice(0,y(e)+1).replace(_,""):e},C=b.Symbol,A=Object.prototype,j=A.hasOwnProperty,T=A.toString,M=C?C.toStringTag:void 0;var S=function(e){var t=j.call(e,M),n=e[M];try{e[M]=void 0;var i=!0}catch(e){}var o=T.call(e);return i&&(t?e[M]=n:delete e[M]),o},E=Object.prototype.toString;var I=function(e){return E.call(e)},O=C?C.toStringTag:void 0;var V=function(e){return null==e?void 0===e?"[object Undefined]":"[object Null]":O&&O in Object(e)?S(e):I(e)};var R=function(e){return null!=e&&"object"==typeof e};var P=function(e){return"symbol"==typeof e||R(e)&&"[object Symbol]"==V(e)},F=/^[-+]0x[0-9a-f]+$/i,N=/^0b[01]+$/i,U=/^0o[0-7]+$/i,B=parseInt;var $=function(e){if("number"==typeof e)return e;if(P(e))return NaN;if(g(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=g(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=k(e);var n=N.test(e);return n||U.test(e)?B(e.slice(2),n?2:8):F.test(e)?NaN:+e},q=Math.max,D=Math.min;var L=function(e,t,n){var i,o,r,s,c,a,d=0,u=!1,l=!1,m=!0;if("function"!=typeof e)throw new TypeError("Expected a function");function f(t){var n=i,r=o;return i=o=void 0,d=t,s=e.apply(r,n)}function h(e){return d=e,c=setTimeout(v,t),u?f(e):s}function p(e){var n=e-a;return void 0===a||n>=t||n<0||l&&e-d>=r}function v(){var e=w();if(p(e))return b(e);c=setTimeout(v,function(e){var n=t-(e-a);return l?D(n,r-(e-d)):n}(e))}function b(e){return c=void 0,m&&i?f(e):(i=o=void 0,s)}function x(){var e=w(),n=p(e);if(i=arguments,o=this,a=e,n){if(void 0===c)return h(a);if(l)return clearTimeout(c),c=setTimeout(v,t),f(a)}return void 0===c&&(c=setTimeout(v,t)),s}return t=$(t)||0,g(n)&&(u=!!n.leading,r=(l="maxWait"in n)?q($(n.maxWait)||0,t):r,m="trailing"in n?!!n.trailing:m),x.cancel=function(){void 0!==c&&clearTimeout(c),d=0,i=a=o=c=void 0},x.flush=function(){return void 0===c?s:b(w())},x};n(7);class K extends f.ListView{constructor(e){super(e),this.extendTemplate({attributes:{class:["ck-mentions"],tabindex:"-1"}})}selectFirst(){this.select(0)}selectNext(){const e=this.selected,t=this.items.getIndex(e);this.select(t+1)}selectPrevious(){const e=this.selected,t=this.items.getIndex(e);this.select(t-1)}select(e){let t=0;e>0&&e<this.items.length?t=e:e<0&&(t=this.items.length-1);const n=this.items.get(t);this.selected!==n&&(this.selected&&this.selected.removeHighlight(),n.highlight(),this.selected=n,this._isItemVisibleInScrolledArea(n)||(this.element.scrollTop=n.element.offsetTop))}executeSelected(){this.selected.fire("execute")}_isItemVisibleInScrolledArea(e){return new o.Rect(this.element).contains(new o.Rect(e.element))}}class W extends f.View{constructor(e,t){super(e),this.template=!1,this.domElement=t,this.domElement.classList.add("ck-button"),this.set("isOn",!1),this.on("change:isOn",(e,t,n)=>{n?(this.domElement.classList.add("ck-on"),this.domElement.classList.remove("ck-off")):(this.domElement.classList.add("ck-off"),this.domElement.classList.remove("ck-on"))}),this.listenTo(this.domElement,"click",()=>{this.fire("execute")})}render(){super.render(),this.element=this.domElement}}class H extends f.ListItemView{highlight(){this.children.first.isOn=!0}removeHighlight(){this.children.first.isOn=!1}}const J=[o.keyCodes.arrowup,o.keyCodes.arrowdown,o.keyCodes.esc],z=[o.keyCodes.enter,o.keyCodes.tab];class G extends i.Plugin{static get pluginName(){return"MentionUI"}static get requires(){return[f.ContextualBalloon]}constructor(e){super(e),this._mentionsView=this._createMentionView(),this._mentionsConfigurations=new Map,this._requestFeedDebounced=L(this._requestFeed,100),e.config.define("mention",{feeds:[]})}init(){const e=this.editor,t=e.config.get("mention.commitKeys")||z,n=J.concat(t);this._balloon=e.plugins.get(f.ContextualBalloon),e.editing.view.document.on("keydown",(e,i)=>{var r;r=i.keyCode,n.includes(r)&&this._isUIVisible&&(i.preventDefault(),e.stop(),i.keyCode==o.keyCodes.arrowdown&&this._mentionsView.selectNext(),i.keyCode==o.keyCodes.arrowup&&this._mentionsView.selectPrevious(),t.includes(i.keyCode)&&this._mentionsView.executeSelected(),i.keyCode==o.keyCodes.esc&&this._hideUIAndRemoveMarker())},{priority:"highest"}),Object(f.clickOutsideHandler)({emitter:this._mentionsView,activator:()=>this._isUIVisible,contextElements:[this._balloon.view.element],callback:()=>this._hideUIAndRemoveMarker()});const i=e.config.get("mention.feeds");for(const e of i){const t=e.feed,n=e.marker;if(!Z(n))throw new o.CKEditorError("mentionconfig-incorrect-marker",null,{marker:n});const i=e.minimumCharacters||0,r="function"==typeof t?t.bind(this.editor):Y(t),s={watcher:this._setupTextWatcherForFeed(n,i),marker:n,feedCallback:r,itemRenderer:e.itemRenderer};this._mentionsConfigurations.set(n,s)}this.on("requestFeed:response",(e,t)=>this._handleFeedResponse(t)),this.on("requestFeed:error",()=>this._hideUIAndRemoveMarker())}destroy(){super.destroy(),this._mentionsView.destroy()}get _isUIVisible(){return this._balloon.visibleView===this._mentionsView}_createMentionView(){const e=this.editor.locale,t=new K(e);return this._items=new o.Collection,t.items.bindTo(this._items).using(n=>{const{item:i,marker:o}=n,r=new H(e),s=this._renderItem(i,o);return s.delegate("execute").to(r),r.children.add(s),r.item=i,r.marker=o,r.on("execute",()=>{t.fire("execute",{item:i,marker:o})}),r}),t.on("execute",(e,t)=>{const n=this.editor,i=n.model,o=t.item,r=t.marker,s=n.model.markers.get("mention"),c=i.createPositionAt(i.document.selection.focus),a=i.createPositionAt(s.getStart()),d=i.createRange(a,c);this._hideUIAndRemoveMarker(),n.execute("mention",{mention:o,text:o.text,marker:r,range:d}),n.editing.view.focus()}),t}_getItemRenderer(e){const{itemRenderer:t}=this._mentionsConfigurations.get(e);return t}_requestFeed(e,t){this._lastRequested=t;const{feedCallback:n}=this._mentionsConfigurations.get(e),i=n(t);i instanceof Promise?i.then(n=>{this._lastRequested==t?this.fire("requestFeed:response",{feed:n,marker:e,feedText:t}):this.fire("requestFeed:discarded",{feed:n,marker:e,feedText:t})}).catch(t=>{this.fire("requestFeed:error",{error:t}),Object(o.logWarning)("mention-feed-callback-error",{marker:e})}):this.fire("requestFeed:response",{feed:i,marker:e,feedText:t})}_setupTextWatcherForFeed(e,t){const n=this.editor,i=new h.TextWatcher(n.model,function(e,t){const n=X(e,t);return e=>n.test(e)}(e,t));i.on("matched",(t,i)=>{const o=n.model.document.selection.focus;if(function(e){const t=e.textNode&&e.textNode.hasAttribute("mention"),n=e.nodeBefore;return t||n&&n.is("$text")&&n.hasAttribute("mention")}(o))return void this._hideUIAndRemoveMarker();const r=function(e,t){const n=X(e,0);return t.match(n)[2]}(e,i.text),s=e.length+r.length,c=o.getShiftedBy(-s),a=o.getShiftedBy(-r.length),d=n.model.createRange(c,a);if(ee(n)){const e=n.model.markers.get("mention");n.model.change(t=>{t.updateMarker(e,{range:d})})}else n.model.change(e=>{e.addMarker("mention",{range:d,usingOperation:!1,affectsData:!1})});this._requestFeedDebounced(e,r)}),i.on("unmatched",()=>{this._hideUIAndRemoveMarker()});const o=n.commands.get("mention");return i.bind("isEnabled").to(o),i}_handleFeedResponse(e){const{feed:t,marker:n}=e;if(!ee(this.editor))return;this._items.clear();for(const e of t){const t="object"!=typeof e?{id:e,text:e}:e;this._items.add({item:t,marker:n})}const i=this.editor.model.markers.get("mention");this._items.length?this._showOrUpdateUI(i):this._hideUIAndRemoveMarker()}_showOrUpdateUI(e){this._isUIVisible?this._balloon.updatePosition(this._getBalloonPanelPositionData(e,this._mentionsView.position)):this._balloon.add({view:this._mentionsView,position:this._getBalloonPanelPositionData(e,this._mentionsView.position),singleViewMode:!0}),this._mentionsView.position=this._balloon.view.position,this._mentionsView.selectFirst()}_hideUIAndRemoveMarker(){this._balloon.hasView(this._mentionsView)&&this._balloon.remove(this._mentionsView),ee(this.editor)&&this.editor.model.change(e=>e.removeMarker("mention")),this._mentionsView.position=void 0}_renderItem(e,t){const n=this.editor;let i,o=e.id;const r=this._getItemRenderer(t);if(r){const t=r(e);"string"!=typeof t?i=new W(n.locale,t):o=t}if(!i){const e=new f.ButtonView(n.locale);e.label=o,e.withText=!0,i=e}return i}_getBalloonPanelPositionData(e,t){const n=this.editor,i=n.editing,r=i.view.domConverter,s=i.mapper;return{target:()=>{let t=e.getRange();"$graveyard"==t.start.root.rootName&&(t=n.model.document.selection.getFirstRange());const i=s.toViewRange(t);return o.Rect.getDomRangeRects(r.viewRangeToDom(i)).pop()},limiter:()=>{const e=this.editor.editing.view,t=e.document.selection.editableElement;return t?e.domConverter.mapViewToDom(t.root):null},positions:Q(t)}}}function Q(e){const t={caret_se:e=>({top:e.bottom+3,left:e.right,name:"caret_se",config:{withArrow:!1}}),caret_ne:(e,t)=>({top:e.top-t.height-3,left:e.right,name:"caret_ne",config:{withArrow:!1}}),caret_sw:(e,t)=>({top:e.bottom+3,left:e.right-t.width,name:"caret_sw",config:{withArrow:!1}}),caret_nw:(e,t)=>({top:e.top-t.height-3,left:e.right-t.width,name:"caret_nw",config:{withArrow:!1}})};return Object.prototype.hasOwnProperty.call(t,e)?[t[e]]:[t.caret_se,t.caret_sw,t.caret_ne,t.caret_nw]}function X(e,t){const n=0==t?"*":`{${t},}`,i=o.env.features.isRegExpUnicodePropertySupported?"\\p{Ps}\\p{Pi}\"'":"\\(\\[{\"'";return new RegExp(`(?:^|[ ${i}])([${e}])([\\S]${n})$`,"u")}function Y(e){return t=>e.filter(e=>("string"==typeof e?e:String(e.id)).toLowerCase().includes(t.toLowerCase())).slice(0,10)}function Z(e){return e&&1==e.length}function ee(e){return e.model.markers.has("mention")}n(10);class te extends i.Plugin{toMentionAttribute(e,t){return a(e,t)}static get pluginName(){return"Mention"}static get requires(){return[s,G]}}}]);
|
@@ -0,0 +1,18 @@
|
|
1
|
+
{
|
2
|
+
"plugins": [
|
3
|
+
{
|
4
|
+
"name": "Mention",
|
5
|
+
"className": "Mention",
|
6
|
+
"description": "Introduces support for autocompleting @mentions and #tags. With this feature enabled, when a user types a pre-configured marker, such as <code>@</code> or <code>#</code>, they get autocompletion suggestions.",
|
7
|
+
"docs": "features/mentions.html",
|
8
|
+
"path": "src/mention.js",
|
9
|
+
"htmlOutput": [
|
10
|
+
{
|
11
|
+
"elements": "span",
|
12
|
+
"classes": "mention",
|
13
|
+
"attributes": "data-mention"
|
14
|
+
}
|
15
|
+
]
|
16
|
+
}
|
17
|
+
]
|
18
|
+
}
|
package/package.json
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
{
|
2
|
+
"name": "@ckeditor/ckeditor5-mention",
|
3
|
+
"version": "30.0.0",
|
4
|
+
"description": "Mention feature for CKEditor 5.",
|
5
|
+
"keywords": [
|
6
|
+
"ckeditor",
|
7
|
+
"ckeditor5",
|
8
|
+
"ckeditor 5",
|
9
|
+
"ckeditor5-feature",
|
10
|
+
"ckeditor5-plugin",
|
11
|
+
"ckeditor5-dll"
|
12
|
+
],
|
13
|
+
"main": "src/index.js",
|
14
|
+
"dependencies": {
|
15
|
+
"ckeditor5": "^30.0.0",
|
16
|
+
"lodash-es": "^4.17.15"
|
17
|
+
},
|
18
|
+
"devDependencies": {
|
19
|
+
"@ckeditor/ckeditor5-basic-styles": "^30.0.0",
|
20
|
+
"@ckeditor/ckeditor5-block-quote": "^30.0.0",
|
21
|
+
"@ckeditor/ckeditor5-clipboard": "^30.0.0",
|
22
|
+
"@ckeditor/ckeditor5-core": "^30.0.0",
|
23
|
+
"@ckeditor/ckeditor5-dev-utils": "^25.4.0",
|
24
|
+
"@ckeditor/ckeditor5-editor-classic": "^30.0.0",
|
25
|
+
"@ckeditor/ckeditor5-engine": "^30.0.0",
|
26
|
+
"@ckeditor/ckeditor5-font": "^30.0.0",
|
27
|
+
"@ckeditor/ckeditor5-link": "^30.0.0",
|
28
|
+
"@ckeditor/ckeditor5-paragraph": "^30.0.0",
|
29
|
+
"@ckeditor/ckeditor5-table": "^30.0.0",
|
30
|
+
"@ckeditor/ckeditor5-theme-lark": "^30.0.0",
|
31
|
+
"@ckeditor/ckeditor5-typing": "^30.0.0",
|
32
|
+
"@ckeditor/ckeditor5-ui": "^30.0.0",
|
33
|
+
"@ckeditor/ckeditor5-undo": "^30.0.0",
|
34
|
+
"@ckeditor/ckeditor5-utils": "^30.0.0",
|
35
|
+
"@ckeditor/ckeditor5-widget": "^30.0.0",
|
36
|
+
"lodash": "^4.17.15",
|
37
|
+
"webpack": "^4.43.0",
|
38
|
+
"webpack-cli": "^3.3.11"
|
39
|
+
},
|
40
|
+
"engines": {
|
41
|
+
"node": ">=12.0.0",
|
42
|
+
"npm": ">=5.7.1"
|
43
|
+
},
|
44
|
+
"author": "CKSource (http://cksource.com/)",
|
45
|
+
"license": "GPL-2.0-or-later",
|
46
|
+
"homepage": "https://ckeditor.com/ckeditor-5",
|
47
|
+
"bugs": "https://github.com/ckeditor/ckeditor5/issues",
|
48
|
+
"repository": {
|
49
|
+
"type": "git",
|
50
|
+
"url": "https://github.com/ckeditor/ckeditor5.git",
|
51
|
+
"directory": "packages/ckeditor5-mention"
|
52
|
+
},
|
53
|
+
"files": [
|
54
|
+
"lang",
|
55
|
+
"src",
|
56
|
+
"theme",
|
57
|
+
"build",
|
58
|
+
"ckeditor5-metadata.json"
|
59
|
+
],
|
60
|
+
"scripts": {
|
61
|
+
"dll:build": "webpack"
|
62
|
+
}
|
63
|
+
}
|
package/src/index.js
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
/**
|
2
|
+
* @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
|
3
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
4
|
+
*/
|
5
|
+
|
6
|
+
/**
|
7
|
+
* @module mention
|
8
|
+
*/
|
9
|
+
|
10
|
+
export { default as Mention } from './mention';
|
11
|
+
export { default as MentionEditing } from './mentionediting';
|
12
|
+
export { default as MentionUI } from './mentionui';
|
package/src/mention.js
ADDED
@@ -0,0 +1,260 @@
|
|
1
|
+
/**
|
2
|
+
* @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
|
3
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
4
|
+
*/
|
5
|
+
|
6
|
+
/**
|
7
|
+
* @module mention/mention
|
8
|
+
*/
|
9
|
+
|
10
|
+
import { Plugin } from 'ckeditor5/src/core';
|
11
|
+
|
12
|
+
import MentionEditing, { _toMentionAttribute } from './mentionediting';
|
13
|
+
import MentionUI from './mentionui';
|
14
|
+
|
15
|
+
import '../theme/mention.css';
|
16
|
+
|
17
|
+
/**
|
18
|
+
* The mention plugin.
|
19
|
+
*
|
20
|
+
* For a detailed overview, check the {@glink features/mentions Mention feature documentation}.
|
21
|
+
*
|
22
|
+
* @extends module:core/plugin~Plugin
|
23
|
+
*/
|
24
|
+
export default class Mention extends Plugin {
|
25
|
+
/**
|
26
|
+
* Creates a mention attribute value from the provided view element and optional data.
|
27
|
+
*
|
28
|
+
* editor.plugins.get( 'Mention' ).toMentionAttribute( viewElement, { userId: '1234' } );
|
29
|
+
*
|
30
|
+
* // For a view element: <span data-mention="@joe">@John Doe</span>
|
31
|
+
* // it will return:
|
32
|
+
* // { id: '@joe', userId: '1234', uid: '7a7bc7...', _text: '@John Doe' }
|
33
|
+
*
|
34
|
+
* @param {module:engine/view/element~Element} viewElement
|
35
|
+
* @param {String|Object} [data] Additional data to be stored in the mention attribute.
|
36
|
+
* @returns {module:mention/mention~MentionAttribute}
|
37
|
+
*/
|
38
|
+
toMentionAttribute( viewElement, data ) {
|
39
|
+
return _toMentionAttribute( viewElement, data );
|
40
|
+
}
|
41
|
+
|
42
|
+
/**
|
43
|
+
* @inheritDoc
|
44
|
+
*/
|
45
|
+
static get pluginName() {
|
46
|
+
return 'Mention';
|
47
|
+
}
|
48
|
+
|
49
|
+
/**
|
50
|
+
* @inheritDoc
|
51
|
+
*/
|
52
|
+
static get requires() {
|
53
|
+
return [ MentionEditing, MentionUI ];
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
/**
|
58
|
+
* The configuration of the {@link module:mention/mention~Mention} feature.
|
59
|
+
*
|
60
|
+
* Read more in {@link module:mention/mention~MentionConfig}.
|
61
|
+
*
|
62
|
+
* @member {module:mention/mention~MentionConfig} module:core/editor/editorconfig~EditorConfig#mention
|
63
|
+
* @type {Array.<module/mention~MentionFeed>}
|
64
|
+
*/
|
65
|
+
|
66
|
+
/**
|
67
|
+
* The configuration of the mention feature.
|
68
|
+
*
|
69
|
+
* Read more about {@glink features/mentions#configuration configuring the mention feature}.
|
70
|
+
*
|
71
|
+
* ClassicEditor
|
72
|
+
* .create( editorElement, {
|
73
|
+
* mention: ... // Mention feature options.
|
74
|
+
* } )
|
75
|
+
* .then( ... )
|
76
|
+
* .catch( ... );
|
77
|
+
*
|
78
|
+
* See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
|
79
|
+
*
|
80
|
+
* @interface MentionConfig
|
81
|
+
*/
|
82
|
+
|
83
|
+
/**
|
84
|
+
* The list of mention feeds supported by the editor.
|
85
|
+
*
|
86
|
+
* ClassicEditor
|
87
|
+
* .create( editorElement, {
|
88
|
+
* plugins: [ Mention, ... ],
|
89
|
+
* mention: {
|
90
|
+
* feeds: [
|
91
|
+
* {
|
92
|
+
* marker: '@',
|
93
|
+
* feed: [ '@Barney', '@Lily', '@Marshall', '@Robin', '@Ted' ]
|
94
|
+
* },
|
95
|
+
* ...
|
96
|
+
* ]
|
97
|
+
* }
|
98
|
+
* } )
|
99
|
+
* .then( ... )
|
100
|
+
* .catch( ... );
|
101
|
+
*
|
102
|
+
* You can provide many mention feeds but they must use different `marker`s.
|
103
|
+
* For example, you can use `'@'` to autocomplete people and `'#'` to autocomplete tags.
|
104
|
+
*
|
105
|
+
* @member {Array.<module:mention/mention~MentionFeed>} module:mention/mention~MentionConfig#feeds
|
106
|
+
*/
|
107
|
+
|
108
|
+
/**
|
109
|
+
* The configuration of the custom commit keys supported by the editor.
|
110
|
+
*
|
111
|
+
* ClassicEditor
|
112
|
+
* .create( editorElement, {
|
113
|
+
* plugins: [ Mention, ... ],
|
114
|
+
* mention: {
|
115
|
+
* // [ Enter, Space ]
|
116
|
+
* commitKeys: [ 13, 32 ]
|
117
|
+
* feeds: [
|
118
|
+
* { ... }
|
119
|
+
* ...
|
120
|
+
* ]
|
121
|
+
* }
|
122
|
+
* } )
|
123
|
+
* .then( ... )
|
124
|
+
* .catch( ... );
|
125
|
+
*
|
126
|
+
* Custom commit keys configuration allows you to customize how users will confirm the selection of mentions from the dropdown list.
|
127
|
+
* You can add as many mention commit keys as you need. For instance, in the snippet above new mentions will be committed by pressing
|
128
|
+
* either <kbd>Enter</kbd> or <kbd>Space</kbd> (13 and 32 key codes respectively).
|
129
|
+
*
|
130
|
+
* @member {Array.<Number>} module:mention/mention~MentionConfig#commitKeys
|
131
|
+
* @default [ 13, 9 ] // [ Enter, Tab ]
|
132
|
+
*/
|
133
|
+
|
134
|
+
/**
|
135
|
+
* The mention feed descriptor. Used in {@link module:mention/mention~MentionConfig `config.mention`}.
|
136
|
+
*
|
137
|
+
* See {@link module:mention/mention~MentionConfig} to learn more.
|
138
|
+
*
|
139
|
+
* // Static configuration.
|
140
|
+
* const mentionFeedPeople = {
|
141
|
+
* marker: '@',
|
142
|
+
* feed: [ '@Alice', '@Bob', ... ],
|
143
|
+
* minimumCharacters: 2
|
144
|
+
* };
|
145
|
+
*
|
146
|
+
* // Simple synchronous callback.
|
147
|
+
* const mentionFeedTags = {
|
148
|
+
* marker: '#',
|
149
|
+
* feed: searchString => {
|
150
|
+
* return tags
|
151
|
+
* // Filter the tags list.
|
152
|
+
* .filter( tag => {
|
153
|
+
* return tag.toLowerCase().includes( queryText.toLowerCase() );
|
154
|
+
* } )
|
155
|
+
* // Return 10 items max - needed for generic queries when the list may contain hundreds of elements.
|
156
|
+
* .slice( 0, 10 );
|
157
|
+
* }
|
158
|
+
* };
|
159
|
+
*
|
160
|
+
* const tags = [ 'wysiwyg', 'rte', 'rich-text-edior', 'collaboration', 'real-time', ... ];
|
161
|
+
*
|
162
|
+
* // Asynchronous callback.
|
163
|
+
* const mentionFeedPlaceholders = {
|
164
|
+
* marker: '$',
|
165
|
+
* feed: searchString => {
|
166
|
+
* return getMatchingPlaceholders( searchString );
|
167
|
+
* }
|
168
|
+
* };
|
169
|
+
*
|
170
|
+
* function getMatchingPlaceholders( searchString ) {
|
171
|
+
* return new Promise( resolve => {
|
172
|
+
* doSomeXHRQuery( result => {
|
173
|
+
* // console.log( result );
|
174
|
+
* // -> [ '$name', '$surname', '$postal', ... ]
|
175
|
+
*
|
176
|
+
* resolve( result );
|
177
|
+
* } );
|
178
|
+
* } );
|
179
|
+
* }
|
180
|
+
*
|
181
|
+
* @typedef {Object} module:mention/mention~MentionFeed
|
182
|
+
* @property {String} [marker] The character which triggers autocompletion for mention. It must be a single character.
|
183
|
+
* @property {Array.<module:mention/mention~MentionFeedItem>|Function} feed Autocomplete items. Provide an array for
|
184
|
+
* a static configuration (the mention feature will show matching items automatically) or a function which returns an array of
|
185
|
+
* matching items (directly, or via a promise). If a function is passed, it is executed in the context of the editor instance.
|
186
|
+
* @property {Number} [minimumCharacters=0] Specifies after how many characters the autocomplete panel should be shown.
|
187
|
+
* @property {Function} [itemRenderer] A function that renders a {@link module:mention/mention~MentionFeedItem}
|
188
|
+
* to the autocomplete panel.
|
189
|
+
*/
|
190
|
+
|
191
|
+
/**
|
192
|
+
* The mention feed item. It may be defined as a string or a plain object.
|
193
|
+
*
|
194
|
+
* When defining a feed item as a plain object, the `id` property is obligatory. Additional properties
|
195
|
+
* can be used when customizing the mention feature bahavior
|
196
|
+
* (see {@glink features/mentions#customizing-the-autocomplete-list "Customizing the autocomplete list"}
|
197
|
+
* and {@glink features/mentions#customizing-the-output "Customizing the output"} sections).
|
198
|
+
*
|
199
|
+
* ClassicEditor
|
200
|
+
* .create( editorElement, {
|
201
|
+
* plugins: [ Mention, ... ],
|
202
|
+
* mention: {
|
203
|
+
* feeds: [
|
204
|
+
* // Feed items as objects.
|
205
|
+
* {
|
206
|
+
* marker: '@',
|
207
|
+
* feed: [
|
208
|
+
* {
|
209
|
+
* id: '@Barney',
|
210
|
+
* fullName: 'Barney Bloom'
|
211
|
+
* },
|
212
|
+
* {
|
213
|
+
* id: '@Lily',
|
214
|
+
* fullName: 'Lily Smith'
|
215
|
+
* },
|
216
|
+
* {
|
217
|
+
* id: '@Marshall',
|
218
|
+
* fullName: 'Marshall McDonald'
|
219
|
+
* },
|
220
|
+
* {
|
221
|
+
* id: '@Robin',
|
222
|
+
* fullName: 'Robin Hood'
|
223
|
+
* },
|
224
|
+
* {
|
225
|
+
* id: '@Ted',
|
226
|
+
* fullName: 'Ted Cruze'
|
227
|
+
* },
|
228
|
+
* // ...
|
229
|
+
* ]
|
230
|
+
* },
|
231
|
+
*
|
232
|
+
* // Feed items as plain strings.
|
233
|
+
* {
|
234
|
+
* marker: '#',
|
235
|
+
* feed: [ 'wysiwyg', 'rte', 'rich-text-edior', 'collaboration', 'real-time', ... ]
|
236
|
+
* },
|
237
|
+
* ]
|
238
|
+
* }
|
239
|
+
* } )
|
240
|
+
* .then( ... )
|
241
|
+
* .catch( ... );
|
242
|
+
*
|
243
|
+
* @typedef {Object|String} module:mention/mention~MentionFeedItem
|
244
|
+
* @property {String} id A unique ID of the mention. It must start with the marker character.
|
245
|
+
* @property {String} [text] Text inserted into the editor when creating a mention.
|
246
|
+
*/
|
247
|
+
|
248
|
+
/**
|
249
|
+
* Represents a mention in the model.
|
250
|
+
*
|
251
|
+
* See {@link module:mention/mention~Mention#toMentionAttribute `Mention#toMentionAttribute()`}.
|
252
|
+
*
|
253
|
+
* @interface module:mention/mention~MentionAttribute
|
254
|
+
* @property {String} id The ID of a mention. It identifies the mention item in the mention feed. There can be multiple mentions
|
255
|
+
* in the document with the same ID (e.g. the same hashtag being mentioned).
|
256
|
+
* @property {String} uid A unique ID of this mention instance. Should be passed as an `option.id` when using
|
257
|
+
* {@link module:engine/view/downcastwriter~DowncastWriter#createAttributeElement writer.createAttributeElement()}.
|
258
|
+
* @property {String} _text Helper property that stores the text of the inserted mention. Used for detecting a broken mention
|
259
|
+
* in the editing area.
|
260
|
+
*/
|
@@ -0,0 +1,150 @@
|
|
1
|
+
/**
|
2
|
+
* @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
|
3
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
4
|
+
*/
|
5
|
+
|
6
|
+
/**
|
7
|
+
* @module mention/mentioncommand
|
8
|
+
*/
|
9
|
+
|
10
|
+
import { Command } from 'ckeditor5/src/core';
|
11
|
+
import { CKEditorError, toMap } from 'ckeditor5/src/utils';
|
12
|
+
|
13
|
+
import { _addMentionAttributes } from './mentionediting';
|
14
|
+
|
15
|
+
/**
|
16
|
+
* The mention command.
|
17
|
+
*
|
18
|
+
* The command is registered by {@link module:mention/mentionediting~MentionEditing} as `'mention'`.
|
19
|
+
*
|
20
|
+
* To insert a mention onto a range, execute the command and specify a mention object with a range to replace:
|
21
|
+
*
|
22
|
+
* const focus = editor.model.document.selection.focus;
|
23
|
+
*
|
24
|
+
* // It will replace one character before the selection focus with the '#1234' text
|
25
|
+
* // with the mention attribute filled with passed attributes.
|
26
|
+
* editor.execute( 'mention', {
|
27
|
+
* marker: '#',
|
28
|
+
* mention: {
|
29
|
+
* id: '#1234',
|
30
|
+
* name: 'Foo',
|
31
|
+
* title: 'Big Foo'
|
32
|
+
* },
|
33
|
+
* range: model.createRange( focus, focus.getShiftedBy( -1 ) )
|
34
|
+
* } );
|
35
|
+
*
|
36
|
+
* // It will replace one character before the selection focus with the 'The "Big Foo"' text
|
37
|
+
* // with the mention attribute filled with passed attributes.
|
38
|
+
* editor.execute( 'mention', {
|
39
|
+
* marker: '#',
|
40
|
+
* mention: {
|
41
|
+
* id: '#1234',
|
42
|
+
* name: 'Foo',
|
43
|
+
* title: 'Big Foo'
|
44
|
+
* },
|
45
|
+
* text: 'The "Big Foo"',
|
46
|
+
* range: model.createRange( focus, focus.getShiftedBy( -1 ) )
|
47
|
+
* } );
|
48
|
+
*
|
49
|
+
* @extends module:core/command~Command
|
50
|
+
*/
|
51
|
+
export default class MentionCommand extends Command {
|
52
|
+
/**
|
53
|
+
* @inheritDoc
|
54
|
+
*/
|
55
|
+
refresh() {
|
56
|
+
const model = this.editor.model;
|
57
|
+
const doc = model.document;
|
58
|
+
|
59
|
+
this.isEnabled = model.schema.checkAttributeInSelection( doc.selection, 'mention' );
|
60
|
+
}
|
61
|
+
|
62
|
+
/**
|
63
|
+
* Executes the command.
|
64
|
+
*
|
65
|
+
* @param {Object} [options] Options for the executed command.
|
66
|
+
* @param {Object|String} options.mention The mention object to insert. When a string is passed, it will be used to create a plain
|
67
|
+
* object with the name attribute that equals the passed string.
|
68
|
+
* @param {String} options.marker The marker character (e.g. `'@'`).
|
69
|
+
* @param {String} [options.text] The text of the inserted mention. Defaults to the full mention string composed from `marker` and
|
70
|
+
* `mention` string or `mention.id` if an object is passed.
|
71
|
+
* @param {module:engine/model/range~Range} [options.range] The range to replace.
|
72
|
+
* Note that the replaced range might be shorter than the inserted text with the mention attribute.
|
73
|
+
* @fires execute
|
74
|
+
*/
|
75
|
+
execute( options ) {
|
76
|
+
const model = this.editor.model;
|
77
|
+
const document = model.document;
|
78
|
+
const selection = document.selection;
|
79
|
+
|
80
|
+
const mentionData = typeof options.mention == 'string' ? { id: options.mention } : options.mention;
|
81
|
+
const mentionID = mentionData.id;
|
82
|
+
|
83
|
+
const range = options.range || selection.getFirstRange();
|
84
|
+
|
85
|
+
const mentionText = options.text || mentionID;
|
86
|
+
|
87
|
+
const mention = _addMentionAttributes( { _text: mentionText, id: mentionID }, mentionData );
|
88
|
+
|
89
|
+
if ( options.marker.length != 1 ) {
|
90
|
+
/**
|
91
|
+
* The marker must be a single character.
|
92
|
+
*
|
93
|
+
* Correct markers: `'@'`, `'#'`.
|
94
|
+
*
|
95
|
+
* Incorrect markers: `'$$'`, `'[@'`.
|
96
|
+
*
|
97
|
+
* See {@link module:mention/mention~MentionConfig}.
|
98
|
+
*
|
99
|
+
* @error mentioncommand-incorrect-marker
|
100
|
+
*/
|
101
|
+
throw new CKEditorError(
|
102
|
+
'mentioncommand-incorrect-marker',
|
103
|
+
this
|
104
|
+
);
|
105
|
+
}
|
106
|
+
|
107
|
+
if ( mentionID.charAt( 0 ) != options.marker ) {
|
108
|
+
/**
|
109
|
+
* The feed item ID must start with the marker character.
|
110
|
+
*
|
111
|
+
* Correct mention feed setting:
|
112
|
+
*
|
113
|
+
* mentions: [
|
114
|
+
* {
|
115
|
+
* marker: '@',
|
116
|
+
* feed: [ '@Ann', '@Barney', ... ]
|
117
|
+
* }
|
118
|
+
* ]
|
119
|
+
*
|
120
|
+
* Incorrect mention feed setting:
|
121
|
+
*
|
122
|
+
* mentions: [
|
123
|
+
* {
|
124
|
+
* marker: '@',
|
125
|
+
* feed: [ 'Ann', 'Barney', ... ]
|
126
|
+
* }
|
127
|
+
* ]
|
128
|
+
*
|
129
|
+
* See {@link module:mention/mention~MentionConfig}.
|
130
|
+
*
|
131
|
+
* @error mentioncommand-incorrect-id
|
132
|
+
*/
|
133
|
+
throw new CKEditorError(
|
134
|
+
'mentioncommand-incorrect-id',
|
135
|
+
this
|
136
|
+
);
|
137
|
+
}
|
138
|
+
|
139
|
+
model.change( writer => {
|
140
|
+
const currentAttributes = toMap( selection.getAttributes() );
|
141
|
+
const attributesWithMention = new Map( currentAttributes.entries() );
|
142
|
+
|
143
|
+
attributesWithMention.set( 'mention', mention );
|
144
|
+
|
145
|
+
// Replace a range with the text with a mention.
|
146
|
+
model.insertContent( writer.createText( mentionText, attributesWithMention ), range );
|
147
|
+
model.insertContent( writer.createText( ' ', currentAttributes ), range.start.getShiftedBy( mentionText.length ) );
|
148
|
+
} );
|
149
|
+
}
|
150
|
+
}
|