Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added skin tone config option #17747

Merged
merged 3 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions packages/ckeditor5-emoji/docs/features/emoji.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,20 @@ ClassicEditor
.catch( /* ... */ );
```

* `skinTone` – Initial skin tone for the emoji picker.

```js
ClassicEditor
.create( document.querySelector( '#editor' ), {
// ... Other configuration options ...
emoji: {
skinTone: 'medium'
}
} )
.then( /* ... */ )
.catch( /* ... */ );
```

<info-box info>
The emoji feature uses the `:` marker that opens a panel with a table of selectable emojis. If you are using {@link features/mentions mentions} or {@link features/merge-fields merge fields} features, they can also show UI panels by pressing a pre-configured key, and it may conflict with the emoji feature. In such a case, the {@link module:emoji/emojimention~EmojiMention} plugin will not integrate the autocompletion mechanism.

Expand Down
21 changes: 21 additions & 0 deletions packages/ckeditor5-emoji/src/emojiconfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
*/

import type { SkinToneId } from './ui/emojitoneview.js';

/**
* @module emoji/emojiconfig
*/
Expand Down Expand Up @@ -43,4 +45,23 @@ export interface EmojiConfig {
* ```
*/
dropdownLimit?: number;

/**
* Initial skin tone for the emoji picker.
*
* ```ts
* ClassicEditor
* .create( editorElement, {
* plugins: [ Emoji, ... ],
* emoji: {
* skinTone: 'medium'
* // More of editor configuration.
* // ...
* }
* } )
* .then( ... )
* .catch( ... );
* ```
*/
skinTone?: SkinToneId;
}
2 changes: 1 addition & 1 deletion packages/ckeditor5-emoji/src/emojimention.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ export default class EmojiMention extends Plugin {
if ( !emojiSkinToneMap ) {
text = null;
} else {
text = emojiSkinToneMap[ this._emojiPickerPlugin.selectedSkinTone ] || emojiSkinToneMap[ 0 ];
text = emojiSkinToneMap[ this._emojiPickerPlugin.selectedSkinTone ] || emojiSkinToneMap.default;
}
}

Expand Down
37 changes: 29 additions & 8 deletions packages/ckeditor5-emoji/src/emojipicker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export default class EmojiPicker extends Plugin {
/**
* Registered emojis. A pair of an emoji name and all its available skin tone variants.
*/
private _emojis: Map<string, Array<string>>;
private _emojis: Map<string, EmojiMap>;

private _selectedSkinTone: SkinToneId;

Expand Down Expand Up @@ -94,8 +94,12 @@ export default class EmojiPicker extends Plugin {
constructor( editor: Editor ) {
super( editor );

this.editor.config.define( 'emoji', {
skinTone: 'default'
} );

this._emojis = new Map();
this._selectedSkinTone = 0;
this._selectedSkinTone = editor.config.get( 'emoji.skinTone' )!;
this._emojiGroups = [];
this._emojiPickerView = null;
this._searchQuery = null;
Expand Down Expand Up @@ -187,10 +191,23 @@ export default class EmojiPicker extends Plugin {
} )
.map( item => {
const name = item.annotation;
const emojis = [ item.unicode ];

if ( 'skins' in item ) {
emojis.push( ...item.skins!.sort( ( a, b ) => a.tone - b.tone ).map( item => item.unicode ) );
const emojis: EmojiMap = { default: item.unicode };

if ( 'skins' in item && item.skins ) {
const skinToneMap: Record<number, SkinToneId> = {
0: 'default',
1: 'light',
2: 'medium-light',
3: 'medium',
4: 'medium-dark',
5: 'dark'
};

item.skins.forEach( skin => {
const skinTone = skinToneMap[ skin.tone ];

emojis[ skinTone ] = skin.unicode;
} );
}

this._emojis.set( name, emojis );
Expand Down Expand Up @@ -315,7 +332,7 @@ export default class EmojiPicker extends Plugin {

private _addTilesToGrid( gridView: EmojiGridView, emojisForCategory: Array<EmojiItem> ) {
for ( const item of emojisForCategory ) {
const emoji = item.emojis[ this._selectedSkinTone ] || item.emojis[ 0 ];
const emoji = item.emojis[ this._selectedSkinTone ] || item.emojis.default;

gridView.tiles.add( gridView.createTile( emoji, item.name ) );
}
Expand Down Expand Up @@ -502,5 +519,9 @@ export type EmojiGroup = {

type EmojiItem = {
name: string;
emojis: Array<string>;
emojis: EmojiMap;
};

type EmojiMap = Partial<Record<SkinToneId, string>> & {
'default': string;
};
38 changes: 22 additions & 16 deletions packages/ckeditor5-emoji/src/ui/emojitoneview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,37 +23,39 @@ import {
DropdownButtonView
} from 'ckeditor5/src/ui.js';

export type SkinToneId = 0 | 1 | 2 | 3 | 4 | 5;
export type SkinToneId = 'default' | 'light' | 'medium-light' | 'medium' | 'medium-dark' | 'dark';

type SkinTone = {
id: SkinToneId;
example: string;
tooltip: string;
};

export default class EmojiToneView extends View {
declare public selectedSkinTone: SkinToneId;

private _skinTones: Array<{
id: SkinToneId;
example: string;
tooltip: string;
}>;
private _skinTones: Array<SkinTone>;

private _mainDropdownButton: DropdownButtonView;
private _dropdownButtons: ViewCollection<ListItemButtonView>;

/**
* @inheritDoc
*/
constructor( locale: Locale, defaultSkinTone: SkinToneId ) {
constructor( locale: Locale, skinTone: SkinToneId ) {
super( locale );

const t = locale.t;

this.set( 'selectedSkinTone', defaultSkinTone );
this.set( 'selectedSkinTone', skinTone );

this._skinTones = [
{ id: 0, example: '👋', tooltip: 'Default skin tone' },
{ id: 1, example: '👋🏻', tooltip: 'Light skin tone' },
{ id: 2, example: '👋🏼', tooltip: 'Medium Light skin tone' },
{ id: 3, example: '👋🏽', tooltip: 'Medium skin tone' },
{ id: 4, example: '👋🏾', tooltip: 'Medium Dark skin tone' },
{ id: 5, example: '👋🏿', tooltip: 'Dark skin tone' }
{ id: 'default', example: '👋', tooltip: 'Default skin tone' },
{ id: 'light', example: '👋🏻', tooltip: 'Light skin tone' },
{ id: 'medium-light', example: '👋🏼', tooltip: 'Medium Light skin tone' },
{ id: 'medium', example: '👋🏽', tooltip: 'Medium skin tone' },
{ id: 'medium-dark', example: '👋🏾', tooltip: 'Medium Dark skin tone' },
{ id: 'dark', example: '👋🏿', tooltip: 'Dark skin tone' }
];

this._mainDropdownButton = new DropdownButtonView();
Expand All @@ -63,7 +65,7 @@ export default class EmojiToneView extends View {
);

this._mainDropdownButton.withText = true;
this._mainDropdownButton.label = this._skinTones[ this.selectedSkinTone ].example;
this._mainDropdownButton.label = this._getSkinTone( this.selectedSkinTone ).example;
this._mainDropdownButton.tooltip = 'Select skin tone';

addToolbarToDropdown(
Expand Down Expand Up @@ -111,9 +113,13 @@ export default class EmojiToneView extends View {
this.listenTo( buttonView, 'execute', () => {
this.selectedSkinTone = buttonSkinToneId;

this._mainDropdownButton.label = this._skinTones[ buttonSkinToneId ].example;
this._mainDropdownButton.label = this._getSkinTone( buttonSkinToneId ).example;
} );

return buttonView;
}

private _getSkinTone( skinToneId: SkinToneId ): SkinTone {
return this._skinTones.find( tone => tone.id === skinToneId )!;
}
}
2 changes: 1 addition & 1 deletion packages/ckeditor5-emoji/tests/emojimention.js
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ describe( 'EmojiMention', () => {
} );

it( 'should return emojis with the proper skin tone when it is selected in the emoji picker plugin', () => {
editor.plugins.get( EmojiPicker )._selectedSkinTone = 5;
editor.plugins.get( EmojiPicker )._selectedSkinTone = 'dark';

return queryEmoji( 'hand_with_index_finger_and_thumb_crossed' ).then( queryResult => {
expect( queryResult.length ).to.equal( 2 );
Expand Down
34 changes: 34 additions & 0 deletions packages/ckeditor5-emoji/tests/emojipicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,40 @@ describe( 'EmojiPicker', () => {
expect( originalFirstEmojiInGridTitle ).to.not.equal( newFirstEmojiInGridTitle );
} );

it( 'should respect the editor config', async () => {
await editor.destroy();

await ClassicEditor
.create( editorElement, {
plugins: [ EmojiPicker, Essentials, Paragraph ],
toolbar: [ 'emoji' ],
menuBar: {
isVisible: true
},
emoji: {
skinTone: 'medium'
}
} )
.then( newEditor => {
editor = newEditor;
emojiPicker = newEditor.plugins.get( EmojiPicker );
} );

clickEmojiToolbarButton();

const secondCategoryButton = document.querySelectorAll( '.ck-emoji-categories > button' )[ 1 ];
secondCategoryButton.click();

// Wait for the emojis to load.
await new Promise( resolve => setTimeout( resolve, 250 ) );

const firstEmojiInGrid = document.querySelector( '.ck-emoji-grid__tiles > button' );

firstEmojiInGrid.click();

expect( getModelData( editor.model ) ).to.equal( '<paragraph>👋🏽[]</paragraph>' );
} );

it( 'should load previous category after reopening the emoji picker', async () => {
clickEmojiToolbarButton();

Expand Down
2 changes: 1 addition & 1 deletion packages/ckeditor5-emoji/tests/ui/emojipickerview.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ describe( 'EmojiPickerView', () => {
};

searchView = new EmojiSearchView( locale );
toneView = new EmojiToneView( locale, 0 );
toneView = new EmojiToneView( locale, 'default' );
categoriesView = new EmojiCategoriesView( locale, [ {} ] );
gridView = new EmojiGridView( locale );
infoView = new EmojiInfoView( locale );
Expand Down
14 changes: 7 additions & 7 deletions packages/ckeditor5-emoji/tests/ui/emojitoneview.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ describe( 'EmojiToneView', () => {
t: val => val
};

emojiToneView = new EmojiToneView( locale, 0 );
emojiToneView = new EmojiToneView( locale, 'default' );
emojiToneView.render();
} );

Expand All @@ -25,11 +25,11 @@ describe( 'EmojiToneView', () => {
} );

it( 'updates the skin tone when #execute event triggers', () => {
expect( emojiToneView.selectedSkinTone ).to.equal( 0 );
expect( emojiToneView.selectedSkinTone ).to.equal( 'default' );

Array.from( emojiToneView._dropdownButtons ).at( -1 ).fire( 'execute' );

expect( emojiToneView.selectedSkinTone ).to.equal( 5 );
expect( emojiToneView.selectedSkinTone ).to.equal( 'dark' );
} );

it( 'displays a check mark next to the active skin tone', () => {
Expand Down Expand Up @@ -58,11 +58,11 @@ describe( 'EmojiToneView', () => {
} );

it( 'sets #selectedSkinTone to value passed to the constructor', () => {
emojiToneView = new EmojiToneView( locale, 0 );
expect( emojiToneView.selectedSkinTone ).to.equal( 0 );
emojiToneView = new EmojiToneView( locale, 'default' );
expect( emojiToneView.selectedSkinTone ).to.equal( 'default' );

emojiToneView = new EmojiToneView( locale, 3 );
expect( emojiToneView.selectedSkinTone ).to.equal( 3 );
emojiToneView = new EmojiToneView( locale, 'medium' );
expect( emojiToneView.selectedSkinTone ).to.equal( 'medium' );
} );
} );

Expand Down