@ckeditor/ckeditor5-emoji 0.0.1 → 44.2.0-alpha.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/CHANGELOG.md +4 -0
- package/LICENSE.md +15 -5
- package/README.md +30 -3
- package/build/emoji.js +5 -0
- package/build/translations/af.js +1 -0
- package/build/translations/ar.js +1 -0
- package/build/translations/ast.js +1 -0
- package/build/translations/az.js +1 -0
- package/build/translations/bg.js +1 -0
- package/build/translations/bn.js +1 -0
- package/build/translations/bs.js +1 -0
- package/build/translations/ca.js +1 -0
- package/build/translations/cs.js +1 -0
- package/build/translations/da.js +1 -0
- package/build/translations/de-ch.js +1 -0
- package/build/translations/de.js +1 -0
- package/build/translations/el.js +1 -0
- package/build/translations/en-au.js +1 -0
- package/build/translations/en-gb.js +1 -0
- package/build/translations/eo.js +1 -0
- package/build/translations/es-co.js +1 -0
- package/build/translations/es.js +1 -0
- package/build/translations/et.js +1 -0
- package/build/translations/eu.js +1 -0
- package/build/translations/fa.js +1 -0
- package/build/translations/fi.js +1 -0
- package/build/translations/fr.js +1 -0
- package/build/translations/gl.js +1 -0
- package/build/translations/gu.js +1 -0
- package/build/translations/he.js +1 -0
- package/build/translations/hi.js +1 -0
- package/build/translations/hr.js +1 -0
- package/build/translations/hu.js +1 -0
- package/build/translations/hy.js +1 -0
- package/build/translations/id.js +1 -0
- package/build/translations/it.js +1 -0
- package/build/translations/ja.js +1 -0
- package/build/translations/jv.js +1 -0
- package/build/translations/kk.js +1 -0
- package/build/translations/km.js +1 -0
- package/build/translations/kn.js +1 -0
- package/build/translations/ko.js +1 -0
- package/build/translations/ku.js +1 -0
- package/build/translations/lt.js +1 -0
- package/build/translations/lv.js +1 -0
- package/build/translations/ms.js +1 -0
- package/build/translations/nb.js +1 -0
- package/build/translations/ne.js +1 -0
- package/build/translations/nl.js +1 -0
- package/build/translations/no.js +1 -0
- package/build/translations/oc.js +1 -0
- package/build/translations/pl.js +1 -0
- package/build/translations/pt-br.js +1 -0
- package/build/translations/pt.js +1 -0
- package/build/translations/ro.js +1 -0
- package/build/translations/ru.js +1 -0
- package/build/translations/si.js +1 -0
- package/build/translations/sk.js +1 -0
- package/build/translations/sl.js +1 -0
- package/build/translations/sq.js +1 -0
- package/build/translations/sr-latn.js +1 -0
- package/build/translations/sr.js +1 -0
- package/build/translations/sv.js +1 -0
- package/build/translations/th.js +1 -0
- package/build/translations/ti.js +1 -0
- package/build/translations/tk.js +1 -0
- package/build/translations/tr.js +1 -0
- package/build/translations/tt.js +1 -0
- package/build/translations/ug.js +1 -0
- package/build/translations/uk.js +1 -0
- package/build/translations/ur.js +1 -0
- package/build/translations/uz.js +1 -0
- package/build/translations/vi.js +1 -0
- package/build/translations/zh-cn.js +1 -0
- package/build/translations/zh.js +1 -0
- package/ckeditor5-metadata.json +32 -0
- package/dist/index-content.css +4 -0
- package/dist/index-editor.css +111 -0
- package/dist/index.css +143 -0
- package/dist/index.css.map +1 -0
- package/dist/index.js +1477 -0
- package/dist/index.js.map +1 -0
- package/dist/translations/af.d.ts +8 -0
- package/dist/translations/af.js +5 -0
- package/dist/translations/af.umd.js +11 -0
- package/dist/translations/ar.d.ts +8 -0
- package/dist/translations/ar.js +5 -0
- package/dist/translations/ar.umd.js +11 -0
- package/dist/translations/ast.d.ts +8 -0
- package/dist/translations/ast.js +5 -0
- package/dist/translations/ast.umd.js +11 -0
- package/dist/translations/az.d.ts +8 -0
- package/dist/translations/az.js +5 -0
- package/dist/translations/az.umd.js +11 -0
- package/dist/translations/bg.d.ts +8 -0
- package/dist/translations/bg.js +5 -0
- package/dist/translations/bg.umd.js +11 -0
- package/dist/translations/bn.d.ts +8 -0
- package/dist/translations/bn.js +5 -0
- package/dist/translations/bn.umd.js +11 -0
- package/dist/translations/bs.d.ts +8 -0
- package/dist/translations/bs.js +5 -0
- package/dist/translations/bs.umd.js +11 -0
- package/dist/translations/ca.d.ts +8 -0
- package/dist/translations/ca.js +5 -0
- package/dist/translations/ca.umd.js +11 -0
- package/dist/translations/cs.d.ts +8 -0
- package/dist/translations/cs.js +5 -0
- package/dist/translations/cs.umd.js +11 -0
- package/dist/translations/da.d.ts +8 -0
- package/dist/translations/da.js +5 -0
- package/dist/translations/da.umd.js +11 -0
- package/dist/translations/de-ch.d.ts +8 -0
- package/dist/translations/de-ch.js +5 -0
- package/dist/translations/de-ch.umd.js +11 -0
- package/dist/translations/de.d.ts +8 -0
- package/dist/translations/de.js +5 -0
- package/dist/translations/de.umd.js +11 -0
- package/dist/translations/el.d.ts +8 -0
- package/dist/translations/el.js +5 -0
- package/dist/translations/el.umd.js +11 -0
- package/dist/translations/en-au.d.ts +8 -0
- package/dist/translations/en-au.js +5 -0
- package/dist/translations/en-au.umd.js +11 -0
- package/dist/translations/en-gb.d.ts +8 -0
- package/dist/translations/en-gb.js +5 -0
- package/dist/translations/en-gb.umd.js +11 -0
- package/dist/translations/en.d.ts +8 -0
- package/dist/translations/en.js +5 -0
- package/dist/translations/en.umd.js +11 -0
- package/dist/translations/eo.d.ts +8 -0
- package/dist/translations/eo.js +5 -0
- package/dist/translations/eo.umd.js +11 -0
- package/dist/translations/es-co.d.ts +8 -0
- package/dist/translations/es-co.js +5 -0
- package/dist/translations/es-co.umd.js +11 -0
- package/dist/translations/es.d.ts +8 -0
- package/dist/translations/es.js +5 -0
- package/dist/translations/es.umd.js +11 -0
- package/dist/translations/et.d.ts +8 -0
- package/dist/translations/et.js +5 -0
- package/dist/translations/et.umd.js +11 -0
- package/dist/translations/eu.d.ts +8 -0
- package/dist/translations/eu.js +5 -0
- package/dist/translations/eu.umd.js +11 -0
- package/dist/translations/fa.d.ts +8 -0
- package/dist/translations/fa.js +5 -0
- package/dist/translations/fa.umd.js +11 -0
- package/dist/translations/fi.d.ts +8 -0
- package/dist/translations/fi.js +5 -0
- package/dist/translations/fi.umd.js +11 -0
- package/dist/translations/fr.d.ts +8 -0
- package/dist/translations/fr.js +5 -0
- package/dist/translations/fr.umd.js +11 -0
- package/dist/translations/gl.d.ts +8 -0
- package/dist/translations/gl.js +5 -0
- package/dist/translations/gl.umd.js +11 -0
- package/dist/translations/gu.d.ts +8 -0
- package/dist/translations/gu.js +5 -0
- package/dist/translations/gu.umd.js +11 -0
- package/dist/translations/he.d.ts +8 -0
- package/dist/translations/he.js +5 -0
- package/dist/translations/he.umd.js +11 -0
- package/dist/translations/hi.d.ts +8 -0
- package/dist/translations/hi.js +5 -0
- package/dist/translations/hi.umd.js +11 -0
- package/dist/translations/hr.d.ts +8 -0
- package/dist/translations/hr.js +5 -0
- package/dist/translations/hr.umd.js +11 -0
- package/dist/translations/hu.d.ts +8 -0
- package/dist/translations/hu.js +5 -0
- package/dist/translations/hu.umd.js +11 -0
- package/dist/translations/hy.d.ts +8 -0
- package/dist/translations/hy.js +5 -0
- package/dist/translations/hy.umd.js +11 -0
- package/dist/translations/id.d.ts +8 -0
- package/dist/translations/id.js +5 -0
- package/dist/translations/id.umd.js +11 -0
- package/dist/translations/it.d.ts +8 -0
- package/dist/translations/it.js +5 -0
- package/dist/translations/it.umd.js +11 -0
- package/dist/translations/ja.d.ts +8 -0
- package/dist/translations/ja.js +5 -0
- package/dist/translations/ja.umd.js +11 -0
- package/dist/translations/jv.d.ts +8 -0
- package/dist/translations/jv.js +5 -0
- package/dist/translations/jv.umd.js +11 -0
- package/dist/translations/kk.d.ts +8 -0
- package/dist/translations/kk.js +5 -0
- package/dist/translations/kk.umd.js +11 -0
- package/dist/translations/km.d.ts +8 -0
- package/dist/translations/km.js +5 -0
- package/dist/translations/km.umd.js +11 -0
- package/dist/translations/kn.d.ts +8 -0
- package/dist/translations/kn.js +5 -0
- package/dist/translations/kn.umd.js +11 -0
- package/dist/translations/ko.d.ts +8 -0
- package/dist/translations/ko.js +5 -0
- package/dist/translations/ko.umd.js +11 -0
- package/dist/translations/ku.d.ts +8 -0
- package/dist/translations/ku.js +5 -0
- package/dist/translations/ku.umd.js +11 -0
- package/dist/translations/lt.d.ts +8 -0
- package/dist/translations/lt.js +5 -0
- package/dist/translations/lt.umd.js +11 -0
- package/dist/translations/lv.d.ts +8 -0
- package/dist/translations/lv.js +5 -0
- package/dist/translations/lv.umd.js +11 -0
- package/dist/translations/ms.d.ts +8 -0
- package/dist/translations/ms.js +5 -0
- package/dist/translations/ms.umd.js +11 -0
- package/dist/translations/nb.d.ts +8 -0
- package/dist/translations/nb.js +5 -0
- package/dist/translations/nb.umd.js +11 -0
- package/dist/translations/ne.d.ts +8 -0
- package/dist/translations/ne.js +5 -0
- package/dist/translations/ne.umd.js +11 -0
- package/dist/translations/nl.d.ts +8 -0
- package/dist/translations/nl.js +5 -0
- package/dist/translations/nl.umd.js +11 -0
- package/dist/translations/no.d.ts +8 -0
- package/dist/translations/no.js +5 -0
- package/dist/translations/no.umd.js +11 -0
- package/dist/translations/oc.d.ts +8 -0
- package/dist/translations/oc.js +5 -0
- package/dist/translations/oc.umd.js +11 -0
- package/dist/translations/pl.d.ts +8 -0
- package/dist/translations/pl.js +5 -0
- package/dist/translations/pl.umd.js +11 -0
- package/dist/translations/pt-br.d.ts +8 -0
- package/dist/translations/pt-br.js +5 -0
- package/dist/translations/pt-br.umd.js +11 -0
- package/dist/translations/pt.d.ts +8 -0
- package/dist/translations/pt.js +5 -0
- package/dist/translations/pt.umd.js +11 -0
- package/dist/translations/ro.d.ts +8 -0
- package/dist/translations/ro.js +5 -0
- package/dist/translations/ro.umd.js +11 -0
- package/dist/translations/ru.d.ts +8 -0
- package/dist/translations/ru.js +5 -0
- package/dist/translations/ru.umd.js +11 -0
- package/dist/translations/si.d.ts +8 -0
- package/dist/translations/si.js +5 -0
- package/dist/translations/si.umd.js +11 -0
- package/dist/translations/sk.d.ts +8 -0
- package/dist/translations/sk.js +5 -0
- package/dist/translations/sk.umd.js +11 -0
- package/dist/translations/sl.d.ts +8 -0
- package/dist/translations/sl.js +5 -0
- package/dist/translations/sl.umd.js +11 -0
- package/dist/translations/sq.d.ts +8 -0
- package/dist/translations/sq.js +5 -0
- package/dist/translations/sq.umd.js +11 -0
- package/dist/translations/sr-latn.d.ts +8 -0
- package/dist/translations/sr-latn.js +5 -0
- package/dist/translations/sr-latn.umd.js +11 -0
- package/dist/translations/sr.d.ts +8 -0
- package/dist/translations/sr.js +5 -0
- package/dist/translations/sr.umd.js +11 -0
- package/dist/translations/sv.d.ts +8 -0
- package/dist/translations/sv.js +5 -0
- package/dist/translations/sv.umd.js +11 -0
- package/dist/translations/th.d.ts +8 -0
- package/dist/translations/th.js +5 -0
- package/dist/translations/th.umd.js +11 -0
- package/dist/translations/ti.d.ts +8 -0
- package/dist/translations/ti.js +5 -0
- package/dist/translations/ti.umd.js +11 -0
- package/dist/translations/tk.d.ts +8 -0
- package/dist/translations/tk.js +5 -0
- package/dist/translations/tk.umd.js +11 -0
- package/dist/translations/tr.d.ts +8 -0
- package/dist/translations/tr.js +5 -0
- package/dist/translations/tr.umd.js +11 -0
- package/dist/translations/tt.d.ts +8 -0
- package/dist/translations/tt.js +5 -0
- package/dist/translations/tt.umd.js +11 -0
- package/dist/translations/ug.d.ts +8 -0
- package/dist/translations/ug.js +5 -0
- package/dist/translations/ug.umd.js +11 -0
- package/dist/translations/uk.d.ts +8 -0
- package/dist/translations/uk.js +5 -0
- package/dist/translations/uk.umd.js +11 -0
- package/dist/translations/ur.d.ts +8 -0
- package/dist/translations/ur.js +5 -0
- package/dist/translations/ur.umd.js +11 -0
- package/dist/translations/uz.d.ts +8 -0
- package/dist/translations/uz.js +5 -0
- package/dist/translations/uz.umd.js +11 -0
- package/dist/translations/vi.d.ts +8 -0
- package/dist/translations/vi.js +5 -0
- package/dist/translations/vi.umd.js +11 -0
- package/dist/translations/zh-cn.d.ts +8 -0
- package/dist/translations/zh-cn.js +5 -0
- package/dist/translations/zh-cn.umd.js +11 -0
- package/dist/translations/zh.d.ts +8 -0
- package/dist/translations/zh.js +5 -0
- package/dist/translations/zh.umd.js +11 -0
- package/lang/contexts.json +24 -0
- package/lang/translations/af.po +100 -0
- package/lang/translations/ar.po +100 -0
- package/lang/translations/ast.po +100 -0
- package/lang/translations/az.po +100 -0
- package/lang/translations/bg.po +100 -0
- package/lang/translations/bn.po +100 -0
- package/lang/translations/bs.po +100 -0
- package/lang/translations/ca.po +100 -0
- package/lang/translations/cs.po +100 -0
- package/lang/translations/da.po +100 -0
- package/lang/translations/de-ch.po +100 -0
- package/lang/translations/de.po +100 -0
- package/lang/translations/el.po +100 -0
- package/lang/translations/en-au.po +100 -0
- package/lang/translations/en-gb.po +100 -0
- package/lang/translations/en.po +100 -0
- package/lang/translations/eo.po +100 -0
- package/lang/translations/es-co.po +100 -0
- package/lang/translations/es.po +100 -0
- package/lang/translations/et.po +100 -0
- package/lang/translations/eu.po +100 -0
- package/lang/translations/fa.po +100 -0
- package/lang/translations/fi.po +100 -0
- package/lang/translations/fr.po +100 -0
- package/lang/translations/gl.po +100 -0
- package/lang/translations/gu.po +100 -0
- package/lang/translations/he.po +100 -0
- package/lang/translations/hi.po +100 -0
- package/lang/translations/hr.po +100 -0
- package/lang/translations/hu.po +100 -0
- package/lang/translations/hy.po +100 -0
- package/lang/translations/id.po +100 -0
- package/lang/translations/it.po +100 -0
- package/lang/translations/ja.po +100 -0
- package/lang/translations/jv.po +100 -0
- package/lang/translations/kk.po +100 -0
- package/lang/translations/km.po +100 -0
- package/lang/translations/kn.po +100 -0
- package/lang/translations/ko.po +100 -0
- package/lang/translations/ku.po +100 -0
- package/lang/translations/lt.po +100 -0
- package/lang/translations/lv.po +100 -0
- package/lang/translations/ms.po +100 -0
- package/lang/translations/nb.po +100 -0
- package/lang/translations/ne.po +100 -0
- package/lang/translations/nl.po +100 -0
- package/lang/translations/no.po +100 -0
- package/lang/translations/oc.po +100 -0
- package/lang/translations/pl.po +100 -0
- package/lang/translations/pt-br.po +100 -0
- package/lang/translations/pt.po +100 -0
- package/lang/translations/ro.po +100 -0
- package/lang/translations/ru.po +100 -0
- package/lang/translations/si.po +100 -0
- package/lang/translations/sk.po +100 -0
- package/lang/translations/sl.po +100 -0
- package/lang/translations/sq.po +100 -0
- package/lang/translations/sr-latn.po +100 -0
- package/lang/translations/sr.po +100 -0
- package/lang/translations/sv.po +100 -0
- package/lang/translations/th.po +100 -0
- package/lang/translations/ti.po +100 -0
- package/lang/translations/tk.po +100 -0
- package/lang/translations/tr.po +100 -0
- package/lang/translations/tt.po +100 -0
- package/lang/translations/ug.po +100 -0
- package/lang/translations/uk.po +100 -0
- package/lang/translations/ur.po +100 -0
- package/lang/translations/uz.po +100 -0
- package/lang/translations/vi.po +100 -0
- package/lang/translations/zh-cn.po +100 -0
- package/lang/translations/zh.po +100 -0
- package/package.json +58 -5
- package/src/augmentation.d.ts +24 -0
- package/src/augmentation.js +5 -0
- package/src/emoji.d.ts +32 -0
- package/src/emoji.js +38 -0
- package/src/emojicommand.d.ts +24 -0
- package/src/emojicommand.js +33 -0
- package/src/emojiconfig.d.ts +80 -0
- package/src/emojiconfig.js +5 -0
- package/src/emojimention.d.ts +68 -0
- package/src/emojimention.js +193 -0
- package/src/emojipicker.d.ts +97 -0
- package/src/emojipicker.js +255 -0
- package/src/emojirepository.d.ts +139 -0
- package/src/emojirepository.js +267 -0
- package/src/index.d.ts +14 -0
- package/src/index.js +13 -0
- package/src/ui/emojicategoriesview.d.ts +68 -0
- package/src/ui/emojicategoriesview.js +131 -0
- package/src/ui/emojigridview.d.ts +140 -0
- package/src/ui/emojigridview.js +183 -0
- package/src/ui/emojipickerview.d.ts +91 -0
- package/src/ui/emojipickerview.js +172 -0
- package/src/ui/emojisearchview.d.ts +51 -0
- package/src/ui/emojisearchview.js +89 -0
- package/src/ui/emojitoneview.d.ts +46 -0
- package/src/ui/emojitoneview.js +89 -0
- package/theme/emojicategories.css +29 -0
- package/theme/emojigrid.css +55 -0
- package/theme/emojipicker.css +32 -0
- package/theme/emojitone.css +21 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* @module emoji/emojipicker
|
|
7
|
+
*/
|
|
8
|
+
import { ButtonView, clickOutsideHandler, ContextualBalloon, Dialog, MenuBarMenuListItemButtonView } from 'ckeditor5/src/ui.js';
|
|
9
|
+
import { icons, Plugin } from 'ckeditor5/src/core.js';
|
|
10
|
+
import { Typing } from 'ckeditor5/src/typing.js';
|
|
11
|
+
import EmojiCommand from './emojicommand.js';
|
|
12
|
+
import EmojiRepository from './emojirepository.js';
|
|
13
|
+
import EmojiPickerView from './ui/emojipickerview.js';
|
|
14
|
+
import '../theme/emojipicker.css';
|
|
15
|
+
const VISUAL_SELECTION_MARKER_NAME = 'emoji-picker';
|
|
16
|
+
/**
|
|
17
|
+
* The emoji picker plugin.
|
|
18
|
+
*
|
|
19
|
+
* Introduces the `'emoji'` dropdown.
|
|
20
|
+
*/
|
|
21
|
+
export default class EmojiPicker extends Plugin {
|
|
22
|
+
/**
|
|
23
|
+
* @inheritDoc
|
|
24
|
+
*/
|
|
25
|
+
static get requires() {
|
|
26
|
+
return [EmojiRepository, ContextualBalloon, Dialog, Typing];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* @inheritDoc
|
|
30
|
+
*/
|
|
31
|
+
static get pluginName() {
|
|
32
|
+
return 'EmojiPicker';
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* @inheritDoc
|
|
36
|
+
*/
|
|
37
|
+
static get isOfficialPlugin() {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* @inheritDoc
|
|
42
|
+
*/
|
|
43
|
+
async init() {
|
|
44
|
+
const editor = this.editor;
|
|
45
|
+
this._balloonPlugin = editor.plugins.get('ContextualBalloon');
|
|
46
|
+
this._emojiRepositoryPlugin = editor.plugins.get('EmojiRepository');
|
|
47
|
+
// Skip registering a button in the toolbar and list item in the menu bar if the emoji repository is not ready.
|
|
48
|
+
if (!await this._emojiRepositoryPlugin.isReady()) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const command = new EmojiCommand(editor);
|
|
52
|
+
editor.commands.add('emoji', command);
|
|
53
|
+
editor.ui.componentFactory.add('emoji', () => {
|
|
54
|
+
const button = this._createButton(ButtonView, command);
|
|
55
|
+
button.set({
|
|
56
|
+
tooltip: true
|
|
57
|
+
});
|
|
58
|
+
return button;
|
|
59
|
+
});
|
|
60
|
+
editor.ui.componentFactory.add('menuBar:emoji', () => {
|
|
61
|
+
return this._createButton(MenuBarMenuListItemButtonView, command);
|
|
62
|
+
});
|
|
63
|
+
this._setupConversion();
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* @inheritDoc
|
|
67
|
+
*/
|
|
68
|
+
destroy() {
|
|
69
|
+
super.destroy();
|
|
70
|
+
if (this.emojiPickerView) {
|
|
71
|
+
this.emojiPickerView.destroy();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Represents an active skin tone. Its value depends on the emoji UI plugin.
|
|
76
|
+
*
|
|
77
|
+
* Before opening the UI for the first time, the returned value is read from the editor configuration.
|
|
78
|
+
* Otherwise, it reflects the user's intention.
|
|
79
|
+
*/
|
|
80
|
+
get skinTone() {
|
|
81
|
+
if (!this.emojiPickerView) {
|
|
82
|
+
return this.editor.config.get('emoji.skinTone');
|
|
83
|
+
}
|
|
84
|
+
return this.emojiPickerView.gridView.skinTone;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Displays the balloon with the emoji picker.
|
|
88
|
+
*
|
|
89
|
+
* @param [searchValue=''] A default query used to filer the grid when opening the UI.
|
|
90
|
+
*/
|
|
91
|
+
showUI(searchValue = '') {
|
|
92
|
+
// Show visual selection on a text when the contextual balloon is displayed.
|
|
93
|
+
// See #17654.
|
|
94
|
+
this._showFakeVisualSelection();
|
|
95
|
+
if (!this.emojiPickerView) {
|
|
96
|
+
this.emojiPickerView = this._createEmojiPickerView();
|
|
97
|
+
}
|
|
98
|
+
if (searchValue) {
|
|
99
|
+
this.emojiPickerView.searchView.setInputValue(searchValue);
|
|
100
|
+
}
|
|
101
|
+
this.emojiPickerView.searchView.search(searchValue);
|
|
102
|
+
if (!this._balloonPlugin.hasView(this.emojiPickerView)) {
|
|
103
|
+
this._balloonPlugin.add({
|
|
104
|
+
view: this.emojiPickerView,
|
|
105
|
+
position: this._getBalloonPositionData()
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
this.emojiPickerView.focus();
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Creates a button for toolbar and menu bar that will show the emoji dialog.
|
|
112
|
+
*/
|
|
113
|
+
_createButton(ViewClass, command) {
|
|
114
|
+
const buttonView = new ViewClass(this.editor.locale);
|
|
115
|
+
const t = this.editor.locale.t;
|
|
116
|
+
buttonView.bind('isEnabled').to(command, 'isEnabled');
|
|
117
|
+
buttonView.set({
|
|
118
|
+
label: t('Emoji'),
|
|
119
|
+
icon: icons.emoji,
|
|
120
|
+
isToggleable: true
|
|
121
|
+
});
|
|
122
|
+
buttonView.on('execute', () => {
|
|
123
|
+
this.showUI();
|
|
124
|
+
});
|
|
125
|
+
return buttonView;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Creates an instance of the `EmojiPickerView` class that represents an emoji balloon.
|
|
129
|
+
*/
|
|
130
|
+
_createEmojiPickerView() {
|
|
131
|
+
const emojiPickerView = new EmojiPickerView(this.editor.locale, {
|
|
132
|
+
emojiCategories: this._emojiRepositoryPlugin.getEmojiCategories(),
|
|
133
|
+
skinTone: this.editor.config.get('emoji.skinTone'),
|
|
134
|
+
skinTones: this._emojiRepositoryPlugin.getSkinTones(),
|
|
135
|
+
getEmojiByQuery: (query) => {
|
|
136
|
+
return this._emojiRepositoryPlugin.getEmojiByQuery(query);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
// Insert an emoji on a tile click.
|
|
140
|
+
this.listenTo(emojiPickerView.gridView, 'execute', (evt, data) => {
|
|
141
|
+
const editor = this.editor;
|
|
142
|
+
const textToInsert = data.emoji;
|
|
143
|
+
this._hideUI();
|
|
144
|
+
editor.execute('insertText', { text: textToInsert });
|
|
145
|
+
});
|
|
146
|
+
// Update the balloon position when layout is changed.
|
|
147
|
+
this.listenTo(emojiPickerView, 'update', () => {
|
|
148
|
+
if (this._balloonPlugin.visibleView === emojiPickerView) {
|
|
149
|
+
this._balloonPlugin.updatePosition();
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
// Close the panel on `Esc` key press when the **actions have focus**.
|
|
153
|
+
emojiPickerView.keystrokes.set('Esc', (data, cancel) => {
|
|
154
|
+
this._hideUI();
|
|
155
|
+
cancel();
|
|
156
|
+
});
|
|
157
|
+
// Close the dialog when clicking outside of it.
|
|
158
|
+
clickOutsideHandler({
|
|
159
|
+
emitter: emojiPickerView,
|
|
160
|
+
contextElements: [this._balloonPlugin.view.element],
|
|
161
|
+
callback: () => this._hideUI(),
|
|
162
|
+
activator: () => this._balloonPlugin.visibleView === emojiPickerView
|
|
163
|
+
});
|
|
164
|
+
return emojiPickerView;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Hides the balloon with the emoji picker.
|
|
168
|
+
*/
|
|
169
|
+
_hideUI() {
|
|
170
|
+
this._balloonPlugin.remove(this.emojiPickerView);
|
|
171
|
+
this.emojiPickerView.searchView.setInputValue('');
|
|
172
|
+
this.editor.editing.view.focus();
|
|
173
|
+
this._hideFakeVisualSelection();
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Registers converters.
|
|
177
|
+
*/
|
|
178
|
+
_setupConversion() {
|
|
179
|
+
const editor = this.editor;
|
|
180
|
+
// Renders a fake visual selection marker on an expanded selection.
|
|
181
|
+
editor.conversion.for('editingDowncast').markerToHighlight({
|
|
182
|
+
model: VISUAL_SELECTION_MARKER_NAME,
|
|
183
|
+
view: {
|
|
184
|
+
classes: ['ck-fake-emoji-selection']
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
// Renders a fake visual selection marker on a collapsed selection.
|
|
188
|
+
editor.conversion.for('editingDowncast').markerToElement({
|
|
189
|
+
model: VISUAL_SELECTION_MARKER_NAME,
|
|
190
|
+
view: (data, { writer }) => {
|
|
191
|
+
if (!data.markerRange.isCollapsed) {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
const markerElement = writer.createUIElement('span');
|
|
195
|
+
writer.addClass(['ck-fake-emoji-selection', 'ck-fake-emoji-selection_collapsed'], markerElement);
|
|
196
|
+
return markerElement;
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Returns positioning options for the {@link #_balloonPlugin}. They control the way the balloon is attached
|
|
202
|
+
* to the target element or selection.
|
|
203
|
+
*/
|
|
204
|
+
_getBalloonPositionData() {
|
|
205
|
+
const view = this.editor.editing.view;
|
|
206
|
+
const viewDocument = view.document;
|
|
207
|
+
// Set a target position by converting view selection range to DOM.
|
|
208
|
+
const target = () => view.domConverter.viewRangeToDom(viewDocument.selection.getFirstRange());
|
|
209
|
+
return {
|
|
210
|
+
target
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Displays a fake visual selection when the contextual balloon is displayed.
|
|
215
|
+
*
|
|
216
|
+
* This adds an 'emoji-picker' marker into the document that is rendered as a highlight on selected text fragment.
|
|
217
|
+
*/
|
|
218
|
+
_showFakeVisualSelection() {
|
|
219
|
+
const model = this.editor.model;
|
|
220
|
+
model.change(writer => {
|
|
221
|
+
const range = model.document.selection.getFirstRange();
|
|
222
|
+
if (model.markers.has(VISUAL_SELECTION_MARKER_NAME)) {
|
|
223
|
+
writer.updateMarker(VISUAL_SELECTION_MARKER_NAME, { range });
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
if (range.start.isAtEnd) {
|
|
227
|
+
const startPosition = range.start.getLastMatchingPosition(({ item }) => !model.schema.isContent(item), { boundaries: range });
|
|
228
|
+
writer.addMarker(VISUAL_SELECTION_MARKER_NAME, {
|
|
229
|
+
usingOperation: false,
|
|
230
|
+
affectsData: false,
|
|
231
|
+
range: writer.createRange(startPosition, range.end)
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
writer.addMarker(VISUAL_SELECTION_MARKER_NAME, {
|
|
236
|
+
usingOperation: false,
|
|
237
|
+
affectsData: false,
|
|
238
|
+
range
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Hides the fake visual selection.
|
|
246
|
+
*/
|
|
247
|
+
_hideFakeVisualSelection() {
|
|
248
|
+
const model = this.editor.model;
|
|
249
|
+
if (model.markers.has(VISUAL_SELECTION_MARKER_NAME)) {
|
|
250
|
+
model.change(writer => {
|
|
251
|
+
writer.removeMarker(VISUAL_SELECTION_MARKER_NAME);
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
4
|
+
*/
|
|
5
|
+
import { Plugin, type Editor } from 'ckeditor5/src/core.js';
|
|
6
|
+
import type { SkinToneId } from './emojiconfig.js';
|
|
7
|
+
/**
|
|
8
|
+
* The emoji repository plugin.
|
|
9
|
+
*
|
|
10
|
+
* Loads the emoji database from URL during plugin initialization and provides utility methods to search it.
|
|
11
|
+
*/
|
|
12
|
+
export default class EmojiRepository extends Plugin {
|
|
13
|
+
/**
|
|
14
|
+
* Emoji database.
|
|
15
|
+
*/
|
|
16
|
+
private _database;
|
|
17
|
+
/**
|
|
18
|
+
* A promise resolved after downloading the emoji database.
|
|
19
|
+
* The promise resolves with `true` when the database is successfully downloaded or `false` otherwise.
|
|
20
|
+
*/
|
|
21
|
+
private _databasePromise;
|
|
22
|
+
/**
|
|
23
|
+
* A callback to resolve the {@link #_databasePromise} to control the return value of this promise.
|
|
24
|
+
*/
|
|
25
|
+
private _databasePromiseResolveCallback;
|
|
26
|
+
/**
|
|
27
|
+
* An instance of the [Fuse.js](https://www.fusejs.io/) library.
|
|
28
|
+
*/
|
|
29
|
+
private _fuseSearch;
|
|
30
|
+
/**
|
|
31
|
+
* @inheritDoc
|
|
32
|
+
*/
|
|
33
|
+
static get pluginName(): "EmojiRepository";
|
|
34
|
+
/**
|
|
35
|
+
* @inheritDoc
|
|
36
|
+
*/
|
|
37
|
+
static get isOfficialPlugin(): true;
|
|
38
|
+
/**
|
|
39
|
+
* @inheritDoc
|
|
40
|
+
*/
|
|
41
|
+
constructor(editor: Editor);
|
|
42
|
+
/**
|
|
43
|
+
* @inheritDoc
|
|
44
|
+
*/
|
|
45
|
+
init(): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Returns an array of emoji entries that match the search query.
|
|
48
|
+
* If the emoji database is not loaded, the [Fuse.js](https://www.fusejs.io/) instance is not created,
|
|
49
|
+
* hence this method returns an empty array.
|
|
50
|
+
*
|
|
51
|
+
* @param searchQuery A search query to match emoji.
|
|
52
|
+
* @returns An array of emoji entries that match the search query.
|
|
53
|
+
*/
|
|
54
|
+
getEmojiByQuery(searchQuery: string): Array<EmojiEntry>;
|
|
55
|
+
/**
|
|
56
|
+
* Groups all emojis by categories.
|
|
57
|
+
* If the emoji database is not loaded, it returns an empty array.
|
|
58
|
+
*
|
|
59
|
+
* @returns An array of emoji entries grouped by categories.
|
|
60
|
+
*/
|
|
61
|
+
getEmojiCategories(): Array<EmojiCategory>;
|
|
62
|
+
/**
|
|
63
|
+
* Returns an array of available skin tones.
|
|
64
|
+
*/
|
|
65
|
+
getSkinTones(): Array<SkinTone>;
|
|
66
|
+
/**
|
|
67
|
+
* Indicates whether the emoji database has been successfully downloaded and the plugin is operational.
|
|
68
|
+
*/
|
|
69
|
+
isReady(): Promise<boolean>;
|
|
70
|
+
/**
|
|
71
|
+
* A function used to check if the given emoji is supported in the operating system.
|
|
72
|
+
*
|
|
73
|
+
* Referenced for unit testing purposes.
|
|
74
|
+
*/
|
|
75
|
+
private static _isEmojiSupported;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Represents a single group of the emoji category, e.g., "Smileys & Expressions".
|
|
79
|
+
*/
|
|
80
|
+
export type EmojiCategory = {
|
|
81
|
+
/**
|
|
82
|
+
* A name of the category.
|
|
83
|
+
*/
|
|
84
|
+
title: string;
|
|
85
|
+
/**
|
|
86
|
+
* An example emoji representing items belonging to the category.
|
|
87
|
+
*/
|
|
88
|
+
icon: string;
|
|
89
|
+
/**
|
|
90
|
+
* Group id used to assign {@link #items}.
|
|
91
|
+
*/
|
|
92
|
+
groupId: number;
|
|
93
|
+
/**
|
|
94
|
+
* An array of emojis.
|
|
95
|
+
*/
|
|
96
|
+
items: Array<EmojiEntry>;
|
|
97
|
+
};
|
|
98
|
+
/**
|
|
99
|
+
* Represents a single item fetched from the CDN.
|
|
100
|
+
*/
|
|
101
|
+
export type EmojiCdnResource = {
|
|
102
|
+
annotation: string;
|
|
103
|
+
emoji: string;
|
|
104
|
+
group: number;
|
|
105
|
+
order: number;
|
|
106
|
+
version: number;
|
|
107
|
+
emoticon?: string;
|
|
108
|
+
shortcodes?: Array<string>;
|
|
109
|
+
skins?: Array<{
|
|
110
|
+
emoji: string;
|
|
111
|
+
tone: number;
|
|
112
|
+
version: number;
|
|
113
|
+
}>;
|
|
114
|
+
tags?: Array<string>;
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* Represents a single emoji item used by the emoji feature.
|
|
118
|
+
*/
|
|
119
|
+
export type EmojiEntry = Omit<EmojiCdnResource, 'skins'> & {
|
|
120
|
+
skins: EmojiMap;
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Represents mapping between a skin tone and its corresponding emoji.
|
|
124
|
+
*
|
|
125
|
+
* The `default` key is always present. Additional values are assigned only if an emoji supports skin tones.
|
|
126
|
+
*/
|
|
127
|
+
export type EmojiMap = {
|
|
128
|
+
[K in Exclude<SkinToneId, 'default'>]?: string;
|
|
129
|
+
} & {
|
|
130
|
+
default: string;
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Represents an emoji skin tone variant.
|
|
134
|
+
*/
|
|
135
|
+
export type SkinTone = {
|
|
136
|
+
id: SkinToneId;
|
|
137
|
+
icon: string;
|
|
138
|
+
tooltip: string;
|
|
139
|
+
};
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* @module emoji/emojirepository
|
|
7
|
+
*/
|
|
8
|
+
import Fuse from 'fuse.js';
|
|
9
|
+
import { groupBy } from 'lodash-es';
|
|
10
|
+
import { Plugin } from 'ckeditor5/src/core.js';
|
|
11
|
+
import { logWarning } from 'ckeditor5/src/utils.js';
|
|
12
|
+
// An endpoint from which the emoji database will be downloaded during plugin initialization.
|
|
13
|
+
// The `{version}` placeholder is replaced with the value from editor config.
|
|
14
|
+
const EMOJI_DATABASE_URL = 'https://cdn.ckeditor.com/ckeditor5/data/emoji/{version}/en.json';
|
|
15
|
+
const SKIN_TONE_MAP = {
|
|
16
|
+
0: 'default',
|
|
17
|
+
1: 'light',
|
|
18
|
+
2: 'medium-light',
|
|
19
|
+
3: 'medium',
|
|
20
|
+
4: 'medium-dark',
|
|
21
|
+
5: 'dark'
|
|
22
|
+
};
|
|
23
|
+
const BASELINE_EMOJI_WIDTH = 24;
|
|
24
|
+
/**
|
|
25
|
+
* The emoji repository plugin.
|
|
26
|
+
*
|
|
27
|
+
* Loads the emoji database from URL during plugin initialization and provides utility methods to search it.
|
|
28
|
+
*/
|
|
29
|
+
class EmojiRepository extends Plugin {
|
|
30
|
+
/**
|
|
31
|
+
* @inheritDoc
|
|
32
|
+
*/
|
|
33
|
+
static get pluginName() {
|
|
34
|
+
return 'EmojiRepository';
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* @inheritDoc
|
|
38
|
+
*/
|
|
39
|
+
static get isOfficialPlugin() {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* @inheritDoc
|
|
44
|
+
*/
|
|
45
|
+
constructor(editor) {
|
|
46
|
+
super(editor);
|
|
47
|
+
this.editor.config.define('emoji', {
|
|
48
|
+
version: 16,
|
|
49
|
+
skinTone: 'default'
|
|
50
|
+
});
|
|
51
|
+
this._database = [];
|
|
52
|
+
this._databasePromise = new Promise(resolve => {
|
|
53
|
+
this._databasePromiseResolveCallback = resolve;
|
|
54
|
+
});
|
|
55
|
+
this._fuseSearch = null;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* @inheritDoc
|
|
59
|
+
*/
|
|
60
|
+
async init() {
|
|
61
|
+
const emojiVersion = this.editor.config.get('emoji.version');
|
|
62
|
+
const emojiDatabaseUrl = EMOJI_DATABASE_URL.replace('{version}', `${emojiVersion}`);
|
|
63
|
+
const emojiDatabase = await loadEmojiDatabase(emojiDatabaseUrl);
|
|
64
|
+
// Skip the initialization if the emoji database download has failed.
|
|
65
|
+
// An empty database prevents the initialization of other dependent plugins, such as `EmojiMention` and `EmojiPicker`.
|
|
66
|
+
if (!emojiDatabase.length) {
|
|
67
|
+
return this._databasePromiseResolveCallback(false);
|
|
68
|
+
}
|
|
69
|
+
const container = createEmojiWidthTestingContainer();
|
|
70
|
+
// Store the emoji database after normalizing the raw data.
|
|
71
|
+
this._database = emojiDatabase
|
|
72
|
+
.filter(item => isEmojiCategoryAllowed(item))
|
|
73
|
+
.filter(item => EmojiRepository._isEmojiSupported(item, container))
|
|
74
|
+
.map(item => normalizeEmojiSkinTone(item));
|
|
75
|
+
container.remove();
|
|
76
|
+
// Create instance of the Fuse.js library with configured weighted search keys and disabled fuzzy search.
|
|
77
|
+
this._fuseSearch = new Fuse(this._database, {
|
|
78
|
+
keys: [
|
|
79
|
+
{ name: 'emoticon', weight: 5 },
|
|
80
|
+
{ name: 'annotation', weight: 3 },
|
|
81
|
+
{ name: 'tags', weight: 1 }
|
|
82
|
+
],
|
|
83
|
+
minMatchCharLength: 2,
|
|
84
|
+
threshold: 0,
|
|
85
|
+
ignoreLocation: true
|
|
86
|
+
});
|
|
87
|
+
return this._databasePromiseResolveCallback(true);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Returns an array of emoji entries that match the search query.
|
|
91
|
+
* If the emoji database is not loaded, the [Fuse.js](https://www.fusejs.io/) instance is not created,
|
|
92
|
+
* hence this method returns an empty array.
|
|
93
|
+
*
|
|
94
|
+
* @param searchQuery A search query to match emoji.
|
|
95
|
+
* @returns An array of emoji entries that match the search query.
|
|
96
|
+
*/
|
|
97
|
+
getEmojiByQuery(searchQuery) {
|
|
98
|
+
if (!this._fuseSearch) {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
const searchQueryTokens = searchQuery.split(/\s/).filter(Boolean);
|
|
102
|
+
// Perform the search only if there is at least two non-white characters next to each other.
|
|
103
|
+
const shouldSearch = searchQueryTokens.some(token => token.length >= 2);
|
|
104
|
+
if (!shouldSearch) {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
return this._fuseSearch
|
|
108
|
+
.search({
|
|
109
|
+
'$or': [
|
|
110
|
+
{
|
|
111
|
+
emoticon: searchQuery
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
'$and': searchQueryTokens.map(token => ({ annotation: token }))
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
'$and': searchQueryTokens.map(token => ({ tags: token }))
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
})
|
|
121
|
+
.map(result => result.item);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Groups all emojis by categories.
|
|
125
|
+
* If the emoji database is not loaded, it returns an empty array.
|
|
126
|
+
*
|
|
127
|
+
* @returns An array of emoji entries grouped by categories.
|
|
128
|
+
*/
|
|
129
|
+
getEmojiCategories() {
|
|
130
|
+
if (!this._database.length) {
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
const { t } = this.editor.locale;
|
|
134
|
+
const categories = [
|
|
135
|
+
{ title: t('Smileys & Expressions'), icon: '😀', groupId: 0 },
|
|
136
|
+
{ title: t('Gestures & People'), icon: '👋', groupId: 1 },
|
|
137
|
+
{ title: t('Animals & Nature'), icon: '🐻', groupId: 3 },
|
|
138
|
+
{ title: t('Food & Drinks'), icon: '🍎', groupId: 4 },
|
|
139
|
+
{ title: t('Travel & Places'), icon: '🚘', groupId: 5 },
|
|
140
|
+
{ title: t('Activities'), icon: '🏀', groupId: 6 },
|
|
141
|
+
{ title: t('Objects'), icon: '💡', groupId: 7 },
|
|
142
|
+
{ title: t('Symbols'), icon: '🟢', groupId: 8 },
|
|
143
|
+
{ title: t('Flags'), icon: '🏁', groupId: 9 }
|
|
144
|
+
];
|
|
145
|
+
const groups = groupBy(this._database, 'group');
|
|
146
|
+
return categories.map(category => {
|
|
147
|
+
return {
|
|
148
|
+
...category,
|
|
149
|
+
items: groups[category.groupId]
|
|
150
|
+
};
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Returns an array of available skin tones.
|
|
155
|
+
*/
|
|
156
|
+
getSkinTones() {
|
|
157
|
+
const { t } = this.editor.locale;
|
|
158
|
+
return [
|
|
159
|
+
{ id: 'default', icon: '👋', tooltip: t('Default skin tone') },
|
|
160
|
+
{ id: 'light', icon: '👋🏻', tooltip: t('Light skin tone') },
|
|
161
|
+
{ id: 'medium-light', icon: '👋🏼', tooltip: t('Medium Light skin tone') },
|
|
162
|
+
{ id: 'medium', icon: '👋🏽', tooltip: t('Medium skin tone') },
|
|
163
|
+
{ id: 'medium-dark', icon: '👋🏾', tooltip: t('Medium Dark skin tone') },
|
|
164
|
+
{ id: 'dark', icon: '👋🏿', tooltip: t('Dark skin tone') }
|
|
165
|
+
];
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Indicates whether the emoji database has been successfully downloaded and the plugin is operational.
|
|
169
|
+
*/
|
|
170
|
+
isReady() {
|
|
171
|
+
return this._databasePromise;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* A function used to check if the given emoji is supported in the operating system.
|
|
176
|
+
*
|
|
177
|
+
* Referenced for unit testing purposes.
|
|
178
|
+
*/
|
|
179
|
+
EmojiRepository._isEmojiSupported = isEmojiSupported;
|
|
180
|
+
export default EmojiRepository;
|
|
181
|
+
/**
|
|
182
|
+
* Makes the HTTP request to download the emoji database.
|
|
183
|
+
*/
|
|
184
|
+
async function loadEmojiDatabase(emojiDatabaseUrl) {
|
|
185
|
+
const result = await fetch(emojiDatabaseUrl)
|
|
186
|
+
.then(response => {
|
|
187
|
+
if (!response.ok) {
|
|
188
|
+
return [];
|
|
189
|
+
}
|
|
190
|
+
return response.json();
|
|
191
|
+
})
|
|
192
|
+
.catch(() => {
|
|
193
|
+
return [];
|
|
194
|
+
});
|
|
195
|
+
if (!result.length) {
|
|
196
|
+
/**
|
|
197
|
+
* Unable to load the emoji database from CDN.
|
|
198
|
+
*
|
|
199
|
+
* TODO: It could be a problem of CKEditor 5 CDN, but also, Content Security Policy that disallow the request.
|
|
200
|
+
* It would be good to explain what to do in such a case.
|
|
201
|
+
*
|
|
202
|
+
* @error emoji-database-load-failed
|
|
203
|
+
*/
|
|
204
|
+
logWarning('emoji-database-load-failed');
|
|
205
|
+
}
|
|
206
|
+
return result;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Creates a div for emoji width testing purposes.
|
|
210
|
+
*/
|
|
211
|
+
function createEmojiWidthTestingContainer() {
|
|
212
|
+
const container = document.createElement('div');
|
|
213
|
+
container.setAttribute('aria-hidden', 'true');
|
|
214
|
+
container.style.position = 'absolute';
|
|
215
|
+
container.style.left = '-9999px';
|
|
216
|
+
container.style.whiteSpace = 'nowrap';
|
|
217
|
+
container.style.fontSize = BASELINE_EMOJI_WIDTH + 'px';
|
|
218
|
+
document.body.appendChild(container);
|
|
219
|
+
return container;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Returns the width of the provided node.
|
|
223
|
+
*/
|
|
224
|
+
function getNodeWidth(container, node) {
|
|
225
|
+
const span = document.createElement('span');
|
|
226
|
+
span.textContent = node;
|
|
227
|
+
container.appendChild(span);
|
|
228
|
+
const nodeWidth = span.offsetWidth;
|
|
229
|
+
container.removeChild(span);
|
|
230
|
+
return nodeWidth;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Checks whether the emoji is supported in the operating system.
|
|
234
|
+
*/
|
|
235
|
+
function isEmojiSupported(item, container) {
|
|
236
|
+
const emojiWidth = getNodeWidth(container, item.emoji);
|
|
237
|
+
// On Windows, some supported emoji are ~50% bigger than the baseline emoji, but what we really want to guard
|
|
238
|
+
// against are the ones that are 2x the size, because those are truly broken (person with red hair = person with
|
|
239
|
+
// floating red wig, black cat = cat with black square, polar bear = bear with snowflake, etc.)
|
|
240
|
+
// So here we set the threshold at 1.8 times the size of the baseline emoji.
|
|
241
|
+
return (emojiWidth / 1.8 < BASELINE_EMOJI_WIDTH) && (emojiWidth >= BASELINE_EMOJI_WIDTH);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Adds default skin tone property to each emoji. If emoji defines other skin tones, they are added as well.
|
|
245
|
+
*/
|
|
246
|
+
function normalizeEmojiSkinTone(item) {
|
|
247
|
+
const entry = {
|
|
248
|
+
...item,
|
|
249
|
+
skins: {
|
|
250
|
+
default: item.emoji
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
if (item.skins) {
|
|
254
|
+
item.skins.forEach(skin => {
|
|
255
|
+
const skinTone = SKIN_TONE_MAP[skin.tone];
|
|
256
|
+
entry.skins[skinTone] = skin.emoji;
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
return entry;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Checks whether the emoji belongs to a group that is allowed.
|
|
263
|
+
*/
|
|
264
|
+
function isEmojiCategoryAllowed(item) {
|
|
265
|
+
// Category group=2 contains skin tones only, which we do not want to render.
|
|
266
|
+
return item.group !== 2;
|
|
267
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* @module emoji
|
|
7
|
+
*/
|
|
8
|
+
export { default as Emoji } from './emoji.js';
|
|
9
|
+
export { default as EmojiMention } from './emojimention.js';
|
|
10
|
+
export { default as EmojiPicker } from './emojipicker.js';
|
|
11
|
+
export { default as EmojiRepository } from './emojirepository.js';
|
|
12
|
+
export { default as EmojiCommand } from './emojicommand.js';
|
|
13
|
+
export type { EmojiConfig } from './emojiconfig.js';
|
|
14
|
+
import './augmentation.js';
|
package/src/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright (c) 2003-2025, CKSource Holding sp. z o.o. All rights reserved.
|
|
3
|
+
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-licensing-options
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* @module emoji
|
|
7
|
+
*/
|
|
8
|
+
export { default as Emoji } from './emoji.js';
|
|
9
|
+
export { default as EmojiMention } from './emojimention.js';
|
|
10
|
+
export { default as EmojiPicker } from './emojipicker.js';
|
|
11
|
+
export { default as EmojiRepository } from './emojirepository.js';
|
|
12
|
+
export { default as EmojiCommand } from './emojicommand.js';
|
|
13
|
+
import './augmentation.js';
|