Skip to content
This repository has been archived by the owner on Sep 11, 2019. It is now read-only.

Added mentions and emoji SDK #249

Merged
merged 17 commits into from
Jun 8, 2018
1 change: 1 addition & 0 deletions dev/builder/samples.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"name": "Working with Document",
"samples": [
"Spell Checking",
"Mentions plugin",
"Source Code Editing",
"Magic Line"
]
Expand Down
2 changes: 1 addition & 1 deletion docs
Submodule docs updated from e1a97c to bc658f
Binary file added samples/assets/mentions/img/m_1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/assets/mentions/img/m_10.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/assets/mentions/img/m_2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/assets/mentions/img/m_3.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/assets/mentions/img/m_4.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/assets/mentions/img/m_5.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/assets/mentions/img/m_6.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/assets/mentions/img/m_7.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/assets/mentions/img/m_8.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/assets/mentions/img/m_9.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/assets/mentions/img/w_1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/assets/mentions/img/w_10.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/assets/mentions/img/w_2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/assets/mentions/img/w_3.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/assets/mentions/img/w_4.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/assets/mentions/img/w_5.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/assets/mentions/img/w_6.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/assets/mentions/img/w_7.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/assets/mentions/img/w_8.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/assets/mentions/img/w_9.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
271 changes: 271 additions & 0 deletions samples/mentions.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
<!DOCTYPE html>
<!--
Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
For licensing, see license.html or https://sdk.ckeditor.com/license.html.
-->
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="Mentions plugin">
<meta name="keywords" content="ckeditor, editor, wysiwyg, mentions, complete, autocomplete, default, configure, configuration, setup, settings, options, customization, customize, customise, customisation, config, modification, modify, change">
<meta name="sdk-samples" content="Mentions plugin">
<title>Mentions plugin</title>
<link href="https://fonts.googleapis.com/css?family=Maven+Pro:700,500,400" rel="stylesheet">
<link href="../template/theme/css/sdk.css" rel="stylesheet">
<script src="../../ckeditor-dev/ckeditor.js"></script>
<script src="assets/picoModal-2.0.1.min.js"></script>
<script src="assets/contentloaded.js"></script>
<script src="assets/simplesample.js"></script>
<script src="assets/beautify-html.js"></script>
<script src="assets/sample.js"></script>
<!--[if lt IE 9]>
<script src="assets/html5shiv.min.js"></script>
<![endif]-->
<link rel="icon" href="../template/theme/img/favicon.ico">
<style data-sample="1">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For sure this stylesheet should not have a data-sample attribute. This attribute denotes code fragments that are shown after using option in the "Get Sample Source Code" section.

Get source asmple code example


.chat {
width: 600px;
}

.posts {
list-style-type: none;
padding: 15px;
margin: 0 !important;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Terrified man

Oh my, avoid !important at all cost. 😨 Increase CSS selector specificity.

border: 1px solid #d1d1d1;
border-bottom: none;
}

.posts li:first-child {
margin-bottom: 15px;
}

.posts .message {
display: inline-block;
margin-left: 20px;
width: 80%;
}

.posts .time {
color: #b7b7b7;
font-size: .9em;
}

.posts .photo {
height: 40px;
vertical-align: top;
border-radius: 15%;
}

.cke_autocomplete_panel {
width: 250px !important;
}

.cke_autocomplete_panel .photo {
display: inline-block;
vertical-align: middle;
height: 30px;
border-radius: 50%;
}

.bullet {
padding-left: 5px;
padding-right: 5px;
color: #b7b7b7;
}

.cke_autocomplete_panel .username {
padding-left: 5px;
}

.cke_autocomplete_panel .fullname {
padding-left: 5px;
color: #b7b7b7;
}

.cke_1 {
margin: 0 !important;
}

.send {
width: 100px;
height: 35px;
border: none;
border-radius: 5px;
background-color: #0287D0;
color: #fafafa !important;
margin-top: 8px;
float: right;
cursor: pointer;
}

.send:hover {
background-color: #027abc
}

.mailto-user {
color: #b7b7b7 !important;
text-decoration: underline !important;
}

.mailto-user:hover {
color: #a0a0a0 !important;
}

.post-content {
margin: 0 !important;
line-height: 19px;;
}
</style>
</head>
<body>
<header class="sdk-header">
</header>
<section class="sdk-container">
<nav class="sdk-sidebar">
</nav>
<section class="sdk-contents">
<h1>Mentions plugin</h1>

<p>
The mentions plugin has been introduced in CKEditor 4.10.0 and it provides smart autocompletion feature for custom text matches based on user input. Every time when user type selected marker he will get information about available, existing options.
</p>

<div class="chat">
<ul class="posts">
<li>
<img src="assets/mentions/img/m_1.jpg" alt="avatar" class="photo" />
<div class="message">
<strong>Charles Flores</strong>
<span class="bullet">•</span>
<a class="mailto-user" href="mailto:cflores@example.com">@cflores</a>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whitespace issue, tab used instead of a space.

<span class="bullet">•</span>
<span class="time">2 hours ago</span>
<p class="post-content">
I’m very surprised to see that we’re not allowed to rate each other’s answers anymore. I think it was a quick and easy way to measure the quality of the content.
</p>
</div>
</li>
<li>
<img src="assets/mentions/img/w_1.jpg" alt="avatar" class="photo" />
<div class="message">
<strong>Mildred Wilson</strong>
<span class="bullet">•</span>
<a class="mailto-user" href="mailto:mwilson@example.com">@mwilson</a>
<span class="bullet">•</span>
<span class="time">4 hours ago</span>
<p class="post-content">
Rating has been discontinued because it caused a decline in content quality rather than its increase. People were providing content that would be upvoted, not the content valuable for the community. There are even “secret” groups now!
</p>
</div>
</li>
</ul>
<div class="editor">
<textarea cols="80" id="editor1" name="editor1" rows="10" data-sample="1" data-sample-short>
I agree with <a href="mailto: mildred@example.com">@mwilson</a>. Rating system made the quality much worse. In the long run, especially when we have more new members, it will be too hard to control.
</textarea>
</div>
<button id="send" class="send">Send</button>
</div>

</section>
</section>

<footer class="sdk-footer">
</footer>

<script data-sample="1" >
var users = [
{ id: 1, avatar: 'm_1', fullname: 'Charles Flores' },
{ id: 2, avatar: 'm_2', fullname: 'Gerald Jackson' },
{ id: 3, avatar: 'm_3', fullname: 'Wayne Reed' },
{ id: 4, avatar: 'm_4', fullname: 'Louis Garcia' },
{ id: 5, avatar: 'm_5', fullname: 'Roy Wilson' },
{ id: 6, avatar: 'm_6', fullname: 'Matthew Nelson' },
{ id: 7, avatar: 'm_7', fullname: 'Randy Williams' },
{ id: 8, avatar: 'm_8', fullname: 'Albert Johnson' },
{ id: 9, avatar: 'm_9', fullname: 'Steve Roberts' },
{ id: 10, avatar: 'm_10', fullname: 'Kevin Evans' },

{ id: 11, avatar: 'w_1', fullname: 'Mildred Wilson' },
{ id: 12, avatar: 'w_2', fullname: 'Melissa Nelson' },
{ id: 13, avatar: 'w_3', fullname: 'Kathleen Allen' },
{ id: 14, avatar: 'w_4', fullname: 'Mary Young' },
{ id: 15, avatar: 'w_5', fullname: 'Ashley Rogers' },
{ id: 16, avatar: 'w_6', fullname: 'Debra Griffin' },
{ id: 17, avatar: 'w_7', fullname: 'Denise Williams' },
{ id: 18, avatar: 'w_8', fullname: 'Amy James' },
{ id: 19, avatar: 'w_9', fullname: 'Ruby Anderson' },
{ id: 20, avatar: 'w_10', fullname: 'Wanda Lee' }
],

buttonEl = document.getElementById( 'send' );

// Lets create some fake usernames from fullnames.
CKEDITOR.tools.array.forEach( users, function( user ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make this code sample as simple as possible, thus please inline user names into an array rather than generate it on the fly using JS logic.

var username = user.fullname.split( ' ' );
username[ 0 ] = username[ 0 ].substring( 0, 1 );
user.username = username.join( '' ).toLowerCase();
} );

CKEDITOR.replace( 'editor1', {
extraPlugins: 'mentions,emoji',
removeDialogTabs: 'link:advanced',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of getting rid of plugins, it could be better to simply whitelist allowed plugins using config.plugins properties.

Just provide bare minimum of plugins, basically those that you now use in the toolbar.

As a result you'll be able to remove even more properties, like removeDialogTabs or resize_enabled.

removePlugins: 'elementspath',
height: 150,
resize_enabled: false,
toolbar: [
{ name: 'document', items: [ 'Undo', 'Redo' ] },
{ name: 'basicstyles', items: [ 'Bold', 'Italic', 'Strike', '-', 'RemoveFormat' ] },
{ name: 'links', items: [ 'Link', 'Unlink' ] }
],
mentions: [
{
feed: dataFeed,
itemTemplate:
'<li data-id="{id}"><img class="photo" src="assets/mentions/img/{avatar}.jpg" />' +
'<strong class="username">{username}</strong>' +
'<span class="fullname">{fullname}</span>' +
'</li>',
outputTemplate: '<a href="mailto:{username}@example.com">@{username}</a><span>&nbsp;</span>',
minChars: 0
}
],
on: {
instanceReady: function( evt ) {
buttonEl.addEventListener( 'click', function() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please inline it with document.getElementById( 'send' ) here.

evt.editor.showNotification( 'Your message has been send! However this is only example chat and we cannot make your post public.', 'success', 8000 );
} );
}
}
} );

function dataFeed( opts, callback ) {
var data = users.filter( function( item ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't find comparing item.fullname.replace( ' ', '' ) with opts.query to be a necessity here. Let it be as simple as possible, so just compare based on user name.

return compareWithQuery( opts.query, item.username ) || compareWithQuery( opts.query, item.fullname.replace( ' ', '' ) );
} );

data = data.sort( function( a, b ) {
var nameA = a.fullname.toUpperCase(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Despite the fact that we're filtering results by username this function sorts results by fullname property, which leads to weird results like that:

Names in mention dropdown displayed out of order

nameB = b.fullname.toUpperCase();

if (nameA < nameB) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function could be much, much simplified. First, don't be afraid to use if, else if else construction if there's a need to.

However in this case you could just reuse localeCompare() function (I'm sure you used it at some point 😉).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also extract name of the property used for filtering, ordering into a variable, so that would result with something like that:

function dataFeed( opts, callback ) {
	var filteredProperty = 'username';,
		data = users.filter( function( item ) {
			return item[ filteredProperty ].indexOf( opts.query.toLowerCase() ) == 0;
		} );

	data = data.sort( function( a, b ) {
		return a[ matchProperty ].localeCompare( b[ matchProperty ], undefined, { sensitivity: 'accent' } );
	} );

	callback( data );
}

return -1;
}

if (nameA > nameB) {
return 1;
}

return 0;
} );

callback( data );
}

function compareWithQuery( query, str ) {
return str.toLowerCase().indexOf( query.toLowerCase() ) == 0;
}
</script>
</body>
</html>