@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 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
+ [![npm version](https://badge.fury.io/js/%40ckeditor%2Fckeditor5-mention.svg)](https://www.npmjs.com/package/@ckeditor/ckeditor5-mention)
5
+ [![Coverage Status](https://coveralls.io/repos/github/ckeditor/ckeditor5/badge.svg?branch=master)](https://coveralls.io/github/ckeditor/ckeditor5?branch=master)
6
+ [![Build Status](https://travis-ci.com/ckeditor/ckeditor5.svg?branch=master)](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).
@@ -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
+ }