Skip to content

Commit

Permalink
Merge branch 'epic/ck/17361' into ck/17782-mention-emoji
Browse files Browse the repository at this point in the history
  • Loading branch information
pomek committed Jan 22, 2025
2 parents 9e407d4 + 61d46ec commit f955b6e
Show file tree
Hide file tree
Showing 18 changed files with 344 additions and 182 deletions.
42 changes: 32 additions & 10 deletions packages/ckeditor5-emoji/src/emojidatabase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ export default class EmojiDatabase extends Plugin {
/**
* Emoji database.
*/
declare private _emojiDatabase: Array<EmojiEntry>;
private _emojiDatabase: Array<EmojiEntry>;

/**
* An instance of the [Fuse.js](https://www.fusejs.io/) library.
*/
declare private _fuseSearch: Fuse<EmojiEntry> | null;
private _fuseSearch: Fuse<EmojiEntry> | null;

/**
* @inheritDoc
Expand Down Expand Up @@ -77,14 +77,20 @@ export default class EmojiDatabase extends Plugin {
* @inheritDoc
*/
public async init(): Promise<void> {
// TODO: Add error handling in case when a database is not loaded.
const container = createEmojiWidthTestingContainer();

const emojiVersion = this.editor.config.get( 'emoji.version' )!;
const emojiDatabaseUrl = EMOJI_DATABASE_URL.replace( '{version}', `${ emojiVersion }` );
const emojiDatabase = await loadEmojiDatabase( emojiDatabaseUrl );

// Skip the initialization if the emoji database download has failed.
// An empty database prevents the initialization of other dependent plugins as well: EmojiMention and EmojiPicker.
if ( !emojiDatabase.length ) {
return;
}

// Store emoji database after normalizing the raw data.
this._emojiDatabase = ( await loadEmojiDatabase( emojiDatabaseUrl ) )
const container = createEmojiWidthTestingContainer();

// Store the emoji database after normalizing the raw data.
this._emojiDatabase = emojiDatabase
.filter( item => isEmojiGroupAllowed( item ) )
.filter( item => EmojiDatabase._isEmojiSupported( item, container ) )
.map( item => normalizeEmojiSkinTone( item ) );
Expand All @@ -106,11 +112,16 @@ export default class EmojiDatabase extends Plugin {

/**
* Returns an array of emoji entries that match the search query.
* If the emoji database is not loaded, it returns an empty array.
*
* @param searchQuery A search query to match emoji.
* @returns An array of emoji entries that match the search query.
*/
public getEmojiBySearchQuery( searchQuery: string ): Array<EmojiEntry> {
if ( !this._fuseSearch ) {
return [];
}

const searchQueryTokens = searchQuery.split( /\s/ ).filter( Boolean );

// Perform the search only if there is at least two non-white characters next to each other.
Expand All @@ -120,7 +131,7 @@ export default class EmojiDatabase extends Plugin {
return [];
}

return this._fuseSearch!
return this._fuseSearch
.search( {
'$or': [
{
Expand All @@ -139,11 +150,11 @@ export default class EmojiDatabase extends Plugin {

/**
* Groups all emojis by categories.
* If the emoji database is not loaded, it returns an empty array for each category.
*
* @returns An array of emoji entries grouped by categories.
*/
public getEmojiGroups(): Array<EmojiCategory> {
const groups = groupBy( this._emojiDatabase, 'group' );
const { t } = this.editor.locale;

const categories = [
Expand All @@ -158,10 +169,14 @@ export default class EmojiDatabase extends Plugin {
{ title: t( 'Flags' ), icon: '🏁', groupId: 9 }
];

const groups = this.isDatabaseLoaded() ?
groupBy( this._emojiDatabase, 'group' ) :
null;

return categories.map( category => {
return {
...category,
items: groups[ category.groupId ]
items: groups ? groups[ category.groupId ] : []
};
} );
}
Expand All @@ -182,6 +197,13 @@ export default class EmojiDatabase extends Plugin {
];
}

/**
* Indicates whether the emoji database has been successfully downloaded and the plugin is operational.
*/
public isDatabaseLoaded(): boolean {
return this._emojiDatabase.length > 0;
}

/**
* A function used to check if the given emoji is supported in the operating system.
*
Expand Down
36 changes: 27 additions & 9 deletions packages/ckeditor5-emoji/src/emojimention.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import { logWarning, type LocaleTranslate } from 'ckeditor5/src/utils.js';
import { Plugin, type Editor } from 'ckeditor5/src/core.js';
import type { MentionFeed, MentionFeedObjectItem } from '@ckeditor/ckeditor5-mention';

import EmojiPicker from './emojipicker.js';
import EmojiDatabase from './emojidatabase.js';
import type EmojiPicker from './emojipicker.js';

const EMOJI_MENTION_MARKER = ':';
const EMOJI_SHOW_ALL_OPTION_ID = ':__EMOJI_SHOW_ALL:';
Expand All @@ -29,6 +29,11 @@ export default class EmojiMention extends Plugin {
*/
declare private _emojiPickerPlugin: EmojiPicker | null;

/**
* An instance of the {@link module:emoji/emojidatabase~EmojiDatabase} plugin.
*/
declare private _emojiDatabasePlugin: EmojiDatabase;

/**
* Defines a number of displayed items in the auto complete dropdown.
*
Expand Down Expand Up @@ -69,8 +74,8 @@ export default class EmojiMention extends Plugin {

this._emojiDropdownLimit = editor.config.get( 'emoji.dropdownLimit' )!;

const mentionFeedsConfigs = this.editor.config.get( 'mention.feeds' )! as Array<MentionFeed>;
const mergeFieldsPrefix = this.editor.config.get( 'mergeFields.prefix' )! as string;
const mentionFeedsConfigs = editor.config.get( 'mention.feeds' )! as Array<MentionFeed>;
const mergeFieldsPrefix = editor.config.get( 'mergeFields.prefix' )! as string;
const markerAlreadyUsed = mentionFeedsConfigs.some( config => config.marker === EMOJI_MENTION_MARKER );
const isMarkerUsedByMergeFields = mergeFieldsPrefix ? mergeFieldsPrefix[ 0 ] === EMOJI_MENTION_MARKER : false;

Expand All @@ -87,14 +92,30 @@ export default class EmojiMention extends Plugin {
}

this._setupMentionConfiguration( mentionFeedsConfigs );
this.editor.once( 'ready', this._overrideMentionExecuteListener.bind( this ) );
}

/**
* @inheritDoc
*/
public init(): void {
this._emojiPickerPlugin = this.editor.plugins.has( EmojiPicker ) ? this.editor.plugins.get( EmojiPicker ) : null;
const editor = this.editor;

this._emojiPickerPlugin = editor.plugins.has( 'EmojiPicker' ) ? editor.plugins.get( 'EmojiPicker' ) : null;
this._emojiDatabasePlugin = editor.plugins.get( 'EmojiDatabase' );
}

/**
* @inheritDoc
*/
public afterInit(): void {
const editor = this.editor;

// Skip overriding the `mention` command listener if the emoji database is not loaded.
if ( !this._emojiDatabasePlugin.isDatabaseLoaded() ) {
return;
}

editor.once( 'ready', this._overrideMentionExecuteListener.bind( this ) );
}

/**
Expand Down Expand Up @@ -197,10 +218,7 @@ export default class EmojiMention extends Plugin {
return [];
}

// TODO: Add error handling if the database was not initialized properly.
const emojiDatabasePlugin = this.editor.plugins.get( 'EmojiDatabase' );

const emojis: Array<MentionFeedObjectItem> = emojiDatabasePlugin.getEmojiBySearchQuery( searchQuery )
const emojis: Array<MentionFeedObjectItem> = this._emojiDatabasePlugin.getEmojiBySearchQuery( searchQuery )
.map( emoji => {
// TODO: The configuration `emoji.skinTone` option is ignored here.
let text = emoji.skins.default;
Expand Down
45 changes: 28 additions & 17 deletions packages/ckeditor5-emoji/src/emojipicker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,20 +62,6 @@ export default class EmojiPicker extends Plugin {
return true;
}

/**
* Represents an active skin tone. Its value depends on the emoji UI plugin.
*
* Before opening the UI for the first time, the returned value is read from the editor configuration.
* Otherwise, it reflects the user's intention.
*/
public get skinTone(): SkinToneId {
if ( !this._emojiPickerView ) {
return this.editor.config.get( 'emoji.skinTone' )!;
}

return this._emojiPickerView.gridView.skinTone;
}

/**
* @inheritDoc
*/
Expand All @@ -90,12 +76,23 @@ export default class EmojiPicker extends Plugin {
/**
* @inheritDoc
*/
public async init(): Promise<void> {
public init(): void {
const editor = this.editor;

// TODO: Add error handling if the database was not initialized properly.
this._emojiDatabase = editor.plugins.get( 'EmojiDatabase' );
this._balloon = editor.plugins.get( ContextualBalloon );
this._balloon = editor.plugins.get( 'ContextualBalloon' );
}

/**
* @inheritDoc
*/
public afterInit(): void {
const editor = this.editor;

// Skip registering a button in the toolbar and list item in the menu bar if the emoji database is not loaded.
if ( !this._emojiDatabase.isDatabaseLoaded() ) {
return;
}

editor.ui.componentFactory.add( 'emoji', () => {
const button = this._createUiComponent( ButtonView );
Expand Down Expand Up @@ -125,6 +122,20 @@ export default class EmojiPicker extends Plugin {
}
}

/**
* Represents an active skin tone. Its value depends on the emoji UI plugin.
*
* Before opening the UI for the first time, the returned value is read from the editor configuration.
* Otherwise, it reflects the user's intention.
*/
public get skinTone(): SkinToneId {
if ( !this._emojiPickerView ) {
return this.editor.config.get( 'emoji.skinTone' )!;
}

return this._emojiPickerView.gridView.skinTone;
}

/**
* Displays the balloon with the emoji picker.
*
Expand Down
5 changes: 3 additions & 2 deletions packages/ckeditor5-emoji/src/ui/emojicategoriesview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type { EmojiCategory } from '../emojidatabase.js';

import '../../theme/emojicategories.css';

const ACTIVE_CATEGORY_CLASS = 'ck-active-category';
const ACTIVE_CATEGORY_CLASS = 'ck-emoji__category-item_active';

/**
* A class representing the navigation part of the emoji UI.
Expand Down Expand Up @@ -70,7 +70,8 @@ export default class EmojiCategoriesView extends View {
this.setTemplate( {
tag: 'div',
attributes: {
class: [ 'ck', 'ck-emoji-categories' ]
class: [ 'ck', 'ck-emoji__categories' ],
role: 'tablist'
},
children: this._buttonViews
} );
Expand Down
10 changes: 6 additions & 4 deletions packages/ckeditor5-emoji/src/ui/emojigridview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,18 +94,20 @@ export default class EmojiGridView extends View<HTMLDivElement> implements Filte
{
tag: 'div',
attributes: {
role: 'grid',
class: [
'ck',
'ck-emoji-grid__tiles'
'ck-emoji__grid'
]
},
children: this.tiles
}
],
attributes: {
role: 'tabpanel',
class: [
'ck',
'ck-emoji-grid',
'ck-emoji__tiles',
// To avoid issues with focus cycling, ignore a grid when it's empty.
bind.if( 'isEmpty', 'ck-hidden', value => value )
]
Expand All @@ -117,7 +119,7 @@ export default class EmojiGridView extends View<HTMLDivElement> implements Filte
focusTracker: this.focusTracker,
gridItems: this.tiles,
numberOfColumns: () => global.window
.getComputedStyle( this.element!.firstChild as Element ) // Responsive .ck-emoji-grid__tiles
.getComputedStyle( this.element!.firstChild as Element ) // Responsive `.ck-emoji-grid__tiles`.
.getPropertyValue( 'grid-template-columns' )
.split( ' ' )
.length,
Expand Down Expand Up @@ -239,7 +241,7 @@ export default class EmojiGridView extends View<HTMLDivElement> implements Filte
tile.set( {
label: emoji,
withText: true,
class: 'ck-emoji-grid__tile'
class: 'ck-emoji__tile'
} );

tile.extendTemplate( {
Expand Down
6 changes: 2 additions & 4 deletions packages/ckeditor5-emoji/src/ui/emojipickerview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export default class EmojiPickerView extends View<HTMLDivElement> {
this.toneView
],
attributes: {
class: [ 'ck', 'ck-emoji-picker-wrapper' ]
class: [ 'ck', 'ck-emoji__search' ]
}
},
this.categoriesView,
Expand All @@ -152,10 +152,8 @@ export default class EmojiPickerView extends View<HTMLDivElement> {
}
],
attributes: {
// Avoid focus loss when the user clicks the area of the grid that is not a button.
// https://github.com/ckeditor/ckeditor5/pull/12319#issuecomment-1231779819
tabindex: '-1',
class: [ 'ck', 'ck-emoji-picker', 'ck-search' ]
class: [ 'ck', 'ck-emoji', 'ck-search' ]
}
} );

Expand Down
10 changes: 9 additions & 1 deletion packages/ckeditor5-emoji/src/ui/emojisearchview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,16 @@ export default class EmojiSearchView extends View {

this.setTemplate( {
tag: 'div',
attributes: {
class: [
'ck',
'ck-search'
],

tabindex: '-1'
},
children: [
this.inputView
this.inputView.queryView
]
} );

Expand Down
2 changes: 1 addition & 1 deletion packages/ckeditor5-emoji/src/ui/emojitoneview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export default class EmojiToneView extends View {
this.setTemplate( {
tag: 'div',
attributes: {
class: [ 'ck', 'ck-emoji-tone' ]
class: [ 'ck', 'ck-emoji__skin-tone' ]
},
children: [ dropdownView ]
} );
Expand Down
Loading

0 comments on commit f955b6e

Please sign in to comment.