@atlaskit/editor-plugin-emoji 0.1.2 → 0.2.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 +6 -0
- package/dist/cjs/commands/insert-emoji.js +47 -0
- package/dist/cjs/index.js +8 -1
- package/dist/cjs/messages.js +15 -0
- package/dist/cjs/nodeviews/emoji.js +34 -0
- package/dist/cjs/plugin.js +409 -0
- package/dist/cjs/pm-plugins/ascii-input-rules.js +235 -0
- package/dist/cjs/ui/Emoji/index.js +19 -0
- package/dist/es2019/commands/insert-emoji.js +40 -0
- package/dist/es2019/index.js +1 -1
- package/dist/es2019/messages.js +8 -0
- package/dist/es2019/nodeviews/emoji.js +29 -0
- package/dist/es2019/plugin.js +381 -0
- package/dist/es2019/pm-plugins/ascii-input-rules.js +168 -0
- package/dist/es2019/ui/Emoji/index.js +13 -0
- package/dist/esm/commands/insert-emoji.js +39 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/messages.js +8 -0
- package/dist/esm/nodeviews/emoji.js +27 -0
- package/dist/esm/plugin.js +391 -0
- package/dist/esm/pm-plugins/ascii-input-rules.js +224 -0
- package/dist/esm/ui/Emoji/index.js +12 -0
- package/dist/types/commands/insert-emoji.d.ts +4 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/messages.d.ts +7 -0
- package/dist/types/nodeviews/emoji.d.ts +7 -0
- package/dist/types/plugin.d.ts +38 -0
- package/dist/types/pm-plugins/ascii-input-rules.d.ts +10 -0
- package/dist/types/ui/Emoji/index.d.ts +4 -0
- package/dist/types-ts4.5/commands/insert-emoji.d.ts +4 -0
- package/dist/types-ts4.5/index.d.ts +1 -0
- package/dist/types-ts4.5/messages.d.ts +7 -0
- package/dist/types-ts4.5/nodeviews/emoji.d.ts +7 -0
- package/dist/types-ts4.5/plugin.d.ts +38 -0
- package/dist/types-ts4.5/pm-plugins/ascii-input-rules.d.ts +10 -0
- package/dist/types-ts4.5/ui/Emoji/index.d.ts +4 -0
- package/package.json +29 -23
- package/report.api.md +3 -0
- package/tmp/api-report-tmp.d.ts +46 -0
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
3
|
+
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { emoji } from '@atlaskit/adf-schema';
|
|
6
|
+
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
|
|
7
|
+
import { toolbarInsertBlockMessages as messages } from '@atlaskit/editor-common/messages';
|
|
8
|
+
import { IconEmoji } from '@atlaskit/editor-common/quick-insert';
|
|
9
|
+
import { getInlineNodeViewProducer } from '@atlaskit/editor-common/react-node-view';
|
|
10
|
+
import { SafePlugin } from '@atlaskit/editor-common/safe-plugin';
|
|
11
|
+
import { TypeAheadAvailableNodes } from '@atlaskit/editor-common/type-ahead';
|
|
12
|
+
import { Fragment } from '@atlaskit/editor-prosemirror/model';
|
|
13
|
+
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
14
|
+
import { EmojiTypeAheadItem, recordSelectionFailedSli, recordSelectionSucceededSli, SearchSort } from '@atlaskit/emoji';
|
|
15
|
+
import { insertEmoji } from './commands/insert-emoji';
|
|
16
|
+
import { EmojiNodeView } from './nodeviews/emoji';
|
|
17
|
+
import { inputRulePlugin as asciiInputRulePlugin } from './pm-plugins/ascii-input-rules';
|
|
18
|
+
export var emojiToTypeaheadItem = function emojiToTypeaheadItem(emoji, emojiProvider) {
|
|
19
|
+
return {
|
|
20
|
+
title: emoji.shortName || '',
|
|
21
|
+
key: emoji.id || emoji.shortName,
|
|
22
|
+
render: function render(_ref) {
|
|
23
|
+
var isSelected = _ref.isSelected,
|
|
24
|
+
onClick = _ref.onClick,
|
|
25
|
+
onHover = _ref.onHover;
|
|
26
|
+
return /*#__PURE__*/React.createElement(EmojiTypeAheadItem, {
|
|
27
|
+
emoji: emoji,
|
|
28
|
+
selected: isSelected,
|
|
29
|
+
onMouseMove: onHover,
|
|
30
|
+
onSelection: onClick,
|
|
31
|
+
emojiProvider: emojiProvider
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
emoji: emoji
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
export function memoize(fn) {
|
|
38
|
+
// Cache results here
|
|
39
|
+
var seen = new Map();
|
|
40
|
+
function memoized(emoji, emojiProvider) {
|
|
41
|
+
// Check cache for hits
|
|
42
|
+
var hit = seen.get(emoji.id || emoji.shortName);
|
|
43
|
+
if (hit) {
|
|
44
|
+
return hit;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Generate new result and cache it
|
|
48
|
+
var result = fn(emoji, emojiProvider);
|
|
49
|
+
seen.set(emoji.id || emoji.shortName, result);
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
call: memoized,
|
|
54
|
+
clear: seen.clear.bind(seen)
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
var memoizedToItem = memoize(emojiToTypeaheadItem);
|
|
58
|
+
export var defaultListLimit = 50;
|
|
59
|
+
var isFullShortName = function isFullShortName(query) {
|
|
60
|
+
return query && query.length > 1 && query.charAt(0) === ':' && query.charAt(query.length - 1) === ':';
|
|
61
|
+
};
|
|
62
|
+
var TRIGGER = ':';
|
|
63
|
+
export var emojiPlugin = function emojiPlugin(_ref2) {
|
|
64
|
+
var _api$analytics5;
|
|
65
|
+
var options = _ref2.config,
|
|
66
|
+
api = _ref2.api;
|
|
67
|
+
var typeAhead = {
|
|
68
|
+
id: TypeAheadAvailableNodes.EMOJI,
|
|
69
|
+
trigger: TRIGGER,
|
|
70
|
+
// Custom regex must have a capture group around trigger
|
|
71
|
+
// so it's possible to use it without needing to scan through all triggers again
|
|
72
|
+
customRegex: '\\(?(:)',
|
|
73
|
+
headless: options ? options.headless : undefined,
|
|
74
|
+
getItems: function getItems(_ref3) {
|
|
75
|
+
var query = _ref3.query,
|
|
76
|
+
editorState = _ref3.editorState;
|
|
77
|
+
var pluginState = getEmojiPluginState(editorState);
|
|
78
|
+
var emojiProvider = pluginState.emojiProvider;
|
|
79
|
+
if (!emojiProvider) {
|
|
80
|
+
return Promise.resolve([]);
|
|
81
|
+
}
|
|
82
|
+
return new Promise(function (resolve) {
|
|
83
|
+
var emojiProviderChangeHandler = {
|
|
84
|
+
result: function result(emojiResult) {
|
|
85
|
+
if (!emojiResult || !emojiResult.emojis) {
|
|
86
|
+
resolve([]);
|
|
87
|
+
} else {
|
|
88
|
+
var emojiItems = emojiResult.emojis.map(function (emoji) {
|
|
89
|
+
return memoizedToItem.call(emoji, emojiProvider);
|
|
90
|
+
});
|
|
91
|
+
resolve(emojiItems);
|
|
92
|
+
}
|
|
93
|
+
emojiProvider.unsubscribe(emojiProviderChangeHandler);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
emojiProvider.subscribe(emojiProviderChangeHandler);
|
|
97
|
+
emojiProvider.filter(TRIGGER.concat(query), {
|
|
98
|
+
limit: defaultListLimit,
|
|
99
|
+
skinTone: emojiProvider.getSelectedTone(),
|
|
100
|
+
sort: !query.length ? SearchSort.UsageFrequency : SearchSort.Default
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
},
|
|
104
|
+
forceSelect: function forceSelect(_ref4) {
|
|
105
|
+
var query = _ref4.query,
|
|
106
|
+
items = _ref4.items,
|
|
107
|
+
editorState = _ref4.editorState;
|
|
108
|
+
var _ref5 = emojiPluginKey.getState(editorState) || {},
|
|
109
|
+
asciiMap = _ref5.asciiMap;
|
|
110
|
+
var normalizedQuery = TRIGGER.concat(query);
|
|
111
|
+
|
|
112
|
+
// if the query has space at the end
|
|
113
|
+
// check the ascii map for emojis
|
|
114
|
+
if (asciiMap && normalizedQuery.length >= 3 && normalizedQuery.endsWith(' ') && asciiMap.has(normalizedQuery.trim())) {
|
|
115
|
+
var _emoji = asciiMap.get(normalizedQuery.trim());
|
|
116
|
+
return {
|
|
117
|
+
title: (_emoji === null || _emoji === void 0 ? void 0 : _emoji.name) || '',
|
|
118
|
+
emoji: _emoji
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
var matchedItem = isFullShortName(normalizedQuery) ? items.find(function (item) {
|
|
122
|
+
return item.title.toLowerCase() === normalizedQuery;
|
|
123
|
+
}) : undefined;
|
|
124
|
+
return matchedItem;
|
|
125
|
+
},
|
|
126
|
+
selectItem: function selectItem(state, item, insert, _ref6) {
|
|
127
|
+
var _api$analytics3;
|
|
128
|
+
var mode = _ref6.mode;
|
|
129
|
+
var _item$emoji = item.emoji,
|
|
130
|
+
_item$emoji$id = _item$emoji.id,
|
|
131
|
+
id = _item$emoji$id === void 0 ? '' : _item$emoji$id,
|
|
132
|
+
fallback = _item$emoji.fallback,
|
|
133
|
+
shortName = _item$emoji.shortName;
|
|
134
|
+
var text = fallback || shortName;
|
|
135
|
+
var emojiPluginState = emojiPluginKey.getState(state);
|
|
136
|
+
if (emojiPluginState.emojiProvider && emojiPluginState.emojiProvider.recordSelection && item.emoji) {
|
|
137
|
+
var _api$analytics$shared, _api$analytics, _api$analytics$shared2, _api$analytics$shared3, _api$analytics2, _api$analytics2$share;
|
|
138
|
+
emojiPluginState.emojiProvider.recordSelection(item.emoji).then(recordSelectionSucceededSli({
|
|
139
|
+
createAnalyticsEvent: (_api$analytics$shared = api === null || api === void 0 ? void 0 : (_api$analytics = api.analytics) === null || _api$analytics === void 0 ? void 0 : (_api$analytics$shared2 = _api$analytics.sharedState.currentState()) === null || _api$analytics$shared2 === void 0 ? void 0 : _api$analytics$shared2.createAnalyticsEvent) !== null && _api$analytics$shared !== void 0 ? _api$analytics$shared : undefined
|
|
140
|
+
})).catch(recordSelectionFailedSli({
|
|
141
|
+
createAnalyticsEvent: (_api$analytics$shared3 = api === null || api === void 0 ? void 0 : (_api$analytics2 = api.analytics) === null || _api$analytics2 === void 0 ? void 0 : (_api$analytics2$share = _api$analytics2.sharedState.currentState()) === null || _api$analytics2$share === void 0 ? void 0 : _api$analytics2$share.createAnalyticsEvent) !== null && _api$analytics$shared3 !== void 0 ? _api$analytics$shared3 : undefined
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
var emojiNode = state.schema.nodes.emoji.createChecked({
|
|
145
|
+
shortName: shortName,
|
|
146
|
+
id: id,
|
|
147
|
+
text: text
|
|
148
|
+
});
|
|
149
|
+
var space = state.schema.text(' ');
|
|
150
|
+
var tr = insert(Fragment.from([emojiNode, space]));
|
|
151
|
+
api === null || api === void 0 ? void 0 : (_api$analytics3 = api.analytics) === null || _api$analytics3 === void 0 ? void 0 : _api$analytics3.actions.attachAnalyticsEvent({
|
|
152
|
+
action: ACTION.INSERTED,
|
|
153
|
+
actionSubject: ACTION_SUBJECT.DOCUMENT,
|
|
154
|
+
actionSubjectId: ACTION_SUBJECT_ID.EMOJI,
|
|
155
|
+
attributes: {
|
|
156
|
+
inputMethod: INPUT_METHOD.TYPEAHEAD
|
|
157
|
+
},
|
|
158
|
+
eventType: EVENT_TYPE.TRACK
|
|
159
|
+
})(tr);
|
|
160
|
+
return tr;
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
return {
|
|
164
|
+
name: 'emoji',
|
|
165
|
+
nodes: function nodes() {
|
|
166
|
+
return [{
|
|
167
|
+
name: 'emoji',
|
|
168
|
+
node: emoji
|
|
169
|
+
}];
|
|
170
|
+
},
|
|
171
|
+
pmPlugins: function pmPlugins() {
|
|
172
|
+
return [{
|
|
173
|
+
name: 'emoji',
|
|
174
|
+
plugin: function plugin(pmPluginFactoryParams) {
|
|
175
|
+
return createEmojiPlugin(pmPluginFactoryParams);
|
|
176
|
+
}
|
|
177
|
+
}, {
|
|
178
|
+
name: 'emojiAsciiInputRule',
|
|
179
|
+
plugin: function plugin(_ref7) {
|
|
180
|
+
var _api$analytics4;
|
|
181
|
+
var schema = _ref7.schema,
|
|
182
|
+
providerFactory = _ref7.providerFactory,
|
|
183
|
+
featureFlags = _ref7.featureFlags;
|
|
184
|
+
return asciiInputRulePlugin(schema, providerFactory, featureFlags, api === null || api === void 0 ? void 0 : (_api$analytics4 = api.analytics) === null || _api$analytics4 === void 0 ? void 0 : _api$analytics4.actions);
|
|
185
|
+
}
|
|
186
|
+
}];
|
|
187
|
+
},
|
|
188
|
+
getSharedState: function getSharedState(editorState) {
|
|
189
|
+
if (!editorState) {
|
|
190
|
+
return undefined;
|
|
191
|
+
}
|
|
192
|
+
return emojiPluginKey.getState(editorState);
|
|
193
|
+
},
|
|
194
|
+
commands: {
|
|
195
|
+
insertEmoji: insertEmoji(api === null || api === void 0 ? void 0 : (_api$analytics5 = api.analytics) === null || _api$analytics5 === void 0 ? void 0 : _api$analytics5.actions)
|
|
196
|
+
},
|
|
197
|
+
pluginsOptions: {
|
|
198
|
+
quickInsert: function quickInsert(_ref8) {
|
|
199
|
+
var formatMessage = _ref8.formatMessage;
|
|
200
|
+
return [{
|
|
201
|
+
id: 'emoji',
|
|
202
|
+
title: formatMessage(messages.emoji),
|
|
203
|
+
description: formatMessage(messages.emojiDescription),
|
|
204
|
+
priority: 500,
|
|
205
|
+
keyshortcut: ':',
|
|
206
|
+
icon: function icon() {
|
|
207
|
+
return /*#__PURE__*/React.createElement(IconEmoji, null);
|
|
208
|
+
},
|
|
209
|
+
action: function action(insert, state) {
|
|
210
|
+
var _api$analytics6;
|
|
211
|
+
var tr = insert(undefined);
|
|
212
|
+
api === null || api === void 0 ? void 0 : api.typeAhead.commands.openTypeAheadAtCursor({
|
|
213
|
+
triggerHandler: typeAhead,
|
|
214
|
+
inputMethod: INPUT_METHOD.QUICK_INSERT
|
|
215
|
+
})({
|
|
216
|
+
tr: tr
|
|
217
|
+
});
|
|
218
|
+
api === null || api === void 0 ? void 0 : (_api$analytics6 = api.analytics) === null || _api$analytics6 === void 0 ? void 0 : _api$analytics6.actions.attachAnalyticsEvent({
|
|
219
|
+
action: ACTION.INVOKED,
|
|
220
|
+
actionSubject: ACTION_SUBJECT.TYPEAHEAD,
|
|
221
|
+
actionSubjectId: ACTION_SUBJECT_ID.TYPEAHEAD_EMOJI,
|
|
222
|
+
attributes: {
|
|
223
|
+
inputMethod: INPUT_METHOD.QUICK_INSERT
|
|
224
|
+
},
|
|
225
|
+
eventType: EVENT_TYPE.UI
|
|
226
|
+
})(tr);
|
|
227
|
+
return tr;
|
|
228
|
+
}
|
|
229
|
+
}];
|
|
230
|
+
},
|
|
231
|
+
typeAhead: typeAhead
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Actions
|
|
238
|
+
*/
|
|
239
|
+
|
|
240
|
+
export var ACTIONS = {
|
|
241
|
+
SET_PROVIDER: 'SET_PROVIDER',
|
|
242
|
+
SET_RESULTS: 'SET_RESULTS',
|
|
243
|
+
SET_ASCII_MAP: 'SET_ASCII_MAP'
|
|
244
|
+
};
|
|
245
|
+
var setAsciiMap = function setAsciiMap(asciiMap) {
|
|
246
|
+
return function (state, dispatch) {
|
|
247
|
+
if (dispatch) {
|
|
248
|
+
dispatch(state.tr.setMeta(emojiPluginKey, {
|
|
249
|
+
action: ACTIONS.SET_ASCII_MAP,
|
|
250
|
+
params: {
|
|
251
|
+
asciiMap: asciiMap
|
|
252
|
+
}
|
|
253
|
+
}));
|
|
254
|
+
}
|
|
255
|
+
return true;
|
|
256
|
+
};
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
*
|
|
261
|
+
* Wrapper to call `onLimitReached` when a specified number of calls of that function
|
|
262
|
+
* have been made within a time period.
|
|
263
|
+
*
|
|
264
|
+
* Note: It does not rate limit
|
|
265
|
+
*
|
|
266
|
+
* @param fn Function to wrap
|
|
267
|
+
* @param limitTime Time limit in milliseconds
|
|
268
|
+
* @param limitCount Number of function calls before `onRateReached` is called (per time period)
|
|
269
|
+
* @returns Wrapped function
|
|
270
|
+
*/
|
|
271
|
+
export function createRateLimitReachedFunction(fn, limitTime, limitCount, onLimitReached) {
|
|
272
|
+
var lastCallTime = 0;
|
|
273
|
+
var callCount = 0;
|
|
274
|
+
return function wrappedFn() {
|
|
275
|
+
var now = Date.now();
|
|
276
|
+
if (now - lastCallTime < limitTime) {
|
|
277
|
+
if (++callCount > limitCount) {
|
|
278
|
+
onLimitReached === null || onLimitReached === void 0 ? void 0 : onLimitReached();
|
|
279
|
+
}
|
|
280
|
+
} else {
|
|
281
|
+
lastCallTime = now;
|
|
282
|
+
callCount = 1;
|
|
283
|
+
}
|
|
284
|
+
return fn.apply(void 0, arguments);
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// At this stage console.error only
|
|
289
|
+
var logRateWarning = function logRateWarning() {
|
|
290
|
+
if (process.env.NODE_ENV === 'development') {
|
|
291
|
+
// eslint-disable-next-line no-console
|
|
292
|
+
console.error('The emoji provider injected in the Editor is being reloaded frequently, this will cause a slow Editor experience.');
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
export var setProvider = createRateLimitReachedFunction(function (provider) {
|
|
296
|
+
return function (state, dispatch) {
|
|
297
|
+
if (dispatch) {
|
|
298
|
+
dispatch(state.tr.setMeta(emojiPluginKey, {
|
|
299
|
+
action: ACTIONS.SET_PROVIDER,
|
|
300
|
+
params: {
|
|
301
|
+
provider: provider
|
|
302
|
+
}
|
|
303
|
+
}));
|
|
304
|
+
}
|
|
305
|
+
return true;
|
|
306
|
+
};
|
|
307
|
+
},
|
|
308
|
+
// If we change the emoji provider more than three times every 5 seconds we should warn.
|
|
309
|
+
// This seems like a really long time but the performance can be that laggy that we don't
|
|
310
|
+
// even get 3 events in 3 seconds and miss this indicator.
|
|
311
|
+
5000, 3, logRateWarning);
|
|
312
|
+
export var emojiPluginKey = new PluginKey('emojiPlugin');
|
|
313
|
+
export function getEmojiPluginState(state) {
|
|
314
|
+
return emojiPluginKey.getState(state) || {};
|
|
315
|
+
}
|
|
316
|
+
export function createEmojiPlugin(pmPluginFactoryParams) {
|
|
317
|
+
return new SafePlugin({
|
|
318
|
+
key: emojiPluginKey,
|
|
319
|
+
state: {
|
|
320
|
+
init: function init() {
|
|
321
|
+
return {};
|
|
322
|
+
},
|
|
323
|
+
apply: function apply(tr, pluginState) {
|
|
324
|
+
var _ref9 = tr.getMeta(emojiPluginKey) || {
|
|
325
|
+
action: null,
|
|
326
|
+
params: null
|
|
327
|
+
},
|
|
328
|
+
action = _ref9.action,
|
|
329
|
+
params = _ref9.params;
|
|
330
|
+
var newPluginState = pluginState;
|
|
331
|
+
switch (action) {
|
|
332
|
+
case ACTIONS.SET_PROVIDER:
|
|
333
|
+
newPluginState = _objectSpread(_objectSpread({}, pluginState), {}, {
|
|
334
|
+
emojiProvider: params.provider
|
|
335
|
+
});
|
|
336
|
+
pmPluginFactoryParams.dispatch(emojiPluginKey, newPluginState);
|
|
337
|
+
return newPluginState;
|
|
338
|
+
case ACTIONS.SET_ASCII_MAP:
|
|
339
|
+
newPluginState = _objectSpread(_objectSpread({}, pluginState), {}, {
|
|
340
|
+
asciiMap: params.asciiMap
|
|
341
|
+
});
|
|
342
|
+
pmPluginFactoryParams.dispatch(emojiPluginKey, newPluginState);
|
|
343
|
+
return newPluginState;
|
|
344
|
+
}
|
|
345
|
+
return newPluginState;
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
props: {
|
|
349
|
+
nodeViews: {
|
|
350
|
+
emoji: getInlineNodeViewProducer({
|
|
351
|
+
pmPluginFactoryParams: pmPluginFactoryParams,
|
|
352
|
+
Component: EmojiNodeView,
|
|
353
|
+
extraComponentProps: {
|
|
354
|
+
providerFactory: pmPluginFactoryParams.providerFactory
|
|
355
|
+
}
|
|
356
|
+
})
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
view: function view(editorView) {
|
|
360
|
+
var providerHandler = function providerHandler(name, providerPromise) {
|
|
361
|
+
switch (name) {
|
|
362
|
+
case 'emojiProvider':
|
|
363
|
+
if (!providerPromise) {
|
|
364
|
+
var _setProvider;
|
|
365
|
+
return setProvider === null || setProvider === void 0 ? void 0 : (_setProvider = setProvider(undefined)) === null || _setProvider === void 0 ? void 0 : _setProvider(editorView.state, editorView.dispatch);
|
|
366
|
+
}
|
|
367
|
+
providerPromise.then(function (provider) {
|
|
368
|
+
var _setProvider2;
|
|
369
|
+
setProvider === null || setProvider === void 0 ? void 0 : (_setProvider2 = setProvider(provider)) === null || _setProvider2 === void 0 ? void 0 : _setProvider2(editorView.state, editorView.dispatch);
|
|
370
|
+
provider.getAsciiMap().then(function (asciiMap) {
|
|
371
|
+
setAsciiMap(asciiMap)(editorView.state, editorView.dispatch);
|
|
372
|
+
});
|
|
373
|
+
}).catch(function () {
|
|
374
|
+
var _setProvider3;
|
|
375
|
+
return setProvider === null || setProvider === void 0 ? void 0 : (_setProvider3 = setProvider(undefined)) === null || _setProvider3 === void 0 ? void 0 : _setProvider3(editorView.state, editorView.dispatch);
|
|
376
|
+
});
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
return;
|
|
380
|
+
};
|
|
381
|
+
pmPluginFactoryParams.providerFactory.subscribe('emojiProvider', providerHandler);
|
|
382
|
+
return {
|
|
383
|
+
destroy: function destroy() {
|
|
384
|
+
if (pmPluginFactoryParams.providerFactory) {
|
|
385
|
+
pmPluginFactoryParams.providerFactory.unsubscribe('emojiProvider', providerHandler);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import _get from "@babel/runtime/helpers/get";
|
|
2
|
+
import _inherits from "@babel/runtime/helpers/inherits";
|
|
3
|
+
import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn";
|
|
4
|
+
import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf";
|
|
5
|
+
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
|
6
|
+
import _createClass from "@babel/runtime/helpers/createClass";
|
|
7
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
8
|
+
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
|
|
9
|
+
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }
|
|
10
|
+
import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
|
|
11
|
+
import { PluginKey } from '@atlaskit/editor-prosemirror/state';
|
|
12
|
+
import { createPlugin, createRule, leafNodeReplacementCharacter } from '@atlaskit/prosemirror-input-rules';
|
|
13
|
+
var matcher;
|
|
14
|
+
export function inputRulePlugin(schema, providerFactory, featureFlags, editorAnalyticsAPI) {
|
|
15
|
+
if (schema.nodes.emoji && providerFactory) {
|
|
16
|
+
initMatcher(providerFactory);
|
|
17
|
+
var asciiEmojiRule = createRule(AsciiEmojiMatcher.REGEX, inputRuleHandler(editorAnalyticsAPI));
|
|
18
|
+
return createPlugin('emoji', [asciiEmojiRule]);
|
|
19
|
+
}
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
function initMatcher(providerFactory) {
|
|
23
|
+
var handleProvider = function handleProvider(_name, provider) {
|
|
24
|
+
if (!provider) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
provider.then(function (emojiProvider) {
|
|
28
|
+
emojiProvider.getAsciiMap().then(function (map) {
|
|
29
|
+
matcher = new RecordingAsciiEmojiMatcher(emojiProvider, map);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
providerFactory.subscribe('emojiProvider', handleProvider);
|
|
34
|
+
}
|
|
35
|
+
var inputRuleHandler = function inputRuleHandler(editorAnalyticsAPI) {
|
|
36
|
+
return function (state, matchParts, start, end) {
|
|
37
|
+
if (!matcher) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
var match = matcher.match(matchParts);
|
|
41
|
+
if (match) {
|
|
42
|
+
var transactionCreator = new AsciiEmojiTransactionCreator(state, match, start, end, editorAnalyticsAPI);
|
|
43
|
+
return transactionCreator.create();
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
var REGEX_LEADING_CAPTURE_INDEX = 1;
|
|
49
|
+
var REGEX_EMOJI_LEADING_PARENTHESES = 2;
|
|
50
|
+
var REGEX_EMOJI_ASCII_CAPTURE_INDEX = 3;
|
|
51
|
+
var REGEX_TRAILING_CAPTURE_INDEX = 4;
|
|
52
|
+
var getLeadingString = function getLeadingString(match) {
|
|
53
|
+
var withParenthesis = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
|
|
54
|
+
return match[REGEX_LEADING_CAPTURE_INDEX] + (withParenthesis ? match[REGEX_EMOJI_LEADING_PARENTHESES] : '');
|
|
55
|
+
};
|
|
56
|
+
var getLeadingStringWithoutParentheses = function getLeadingStringWithoutParentheses(match) {
|
|
57
|
+
return getLeadingString(match, false);
|
|
58
|
+
};
|
|
59
|
+
var getAscii = function getAscii(match) {
|
|
60
|
+
var withParentheses = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
|
|
61
|
+
return (withParentheses ? match[REGEX_EMOJI_LEADING_PARENTHESES] : '') + match[REGEX_EMOJI_ASCII_CAPTURE_INDEX].trim();
|
|
62
|
+
};
|
|
63
|
+
var getAsciiWithParentheses = function getAsciiWithParentheses(matchParts) {
|
|
64
|
+
return getAscii(matchParts, true);
|
|
65
|
+
};
|
|
66
|
+
var getTrailingString = function getTrailingString(match) {
|
|
67
|
+
return match[REGEX_TRAILING_CAPTURE_INDEX] || '';
|
|
68
|
+
};
|
|
69
|
+
var AsciiEmojiMatcher = /*#__PURE__*/function () {
|
|
70
|
+
function AsciiEmojiMatcher(asciiToEmojiMap) {
|
|
71
|
+
_classCallCheck(this, AsciiEmojiMatcher);
|
|
72
|
+
this.asciiToEmojiMap = asciiToEmojiMap;
|
|
73
|
+
}
|
|
74
|
+
_createClass(AsciiEmojiMatcher, [{
|
|
75
|
+
key: "match",
|
|
76
|
+
value: function match(matchParts) {
|
|
77
|
+
return this.getAsciiEmojiMatch(getLeadingStringWithoutParentheses(matchParts), getAsciiWithParentheses(matchParts), getTrailingString(matchParts)) || this.getAsciiEmojiMatch(getLeadingString(matchParts), getAscii(matchParts), getTrailingString(matchParts));
|
|
78
|
+
}
|
|
79
|
+
}, {
|
|
80
|
+
key: "getAsciiEmojiMatch",
|
|
81
|
+
value: function getAsciiEmojiMatch(leading, ascii, trailing) {
|
|
82
|
+
var emoji = this.asciiToEmojiMap.get(ascii);
|
|
83
|
+
return emoji ? {
|
|
84
|
+
emoji: emoji,
|
|
85
|
+
leadingString: leading,
|
|
86
|
+
trailingString: trailing
|
|
87
|
+
} : undefined;
|
|
88
|
+
}
|
|
89
|
+
}]);
|
|
90
|
+
return AsciiEmojiMatcher;
|
|
91
|
+
}();
|
|
92
|
+
/**
|
|
93
|
+
* A matcher that will record ascii matches as usages of the matched emoji.
|
|
94
|
+
*/
|
|
95
|
+
/**
|
|
96
|
+
* This regex matches 2 scenarios:
|
|
97
|
+
* 1. an emoticon starting with a colon character (e.g. :D => 😃)
|
|
98
|
+
* 2. an emoticon not starting with a colon character (e.g. 8-D => 😎)
|
|
99
|
+
*
|
|
100
|
+
* Explanation (${leafNodeReplacementCharacter} is replaced with character \ufffc)
|
|
101
|
+
*
|
|
102
|
+
* 1st Capturing Group ((?:^|[\s\ufffc])(?:\(*?))
|
|
103
|
+
* Non-capturing group (?:^|[\s\ufffc])
|
|
104
|
+
* 1st Alternative ^
|
|
105
|
+
* ^ asserts position at start of the string
|
|
106
|
+
* 2nd Alternative [\s\ufffc]
|
|
107
|
+
* matches a single character present in [\s\ufffc]
|
|
108
|
+
* Non-capturing group (?:\(*?)
|
|
109
|
+
* matches the character ( literally between zero and unlimited times, as few times as possible, expanding as needed (lazy)
|
|
110
|
+
* 2nd Capturing Group (\(?)
|
|
111
|
+
* matches a single ( if present
|
|
112
|
+
* 3rd Capturing Group ([^:\s\ufffc\(]\S{1,3}|:\S{1,3}( ))
|
|
113
|
+
* 1st Alternative [^:\s\ufffc\(]\S{1,3}
|
|
114
|
+
* matches a single character not present in [^:\s\ufffc\(] between 1 and 3 times, as many times as possible, giving back as needed (greedy)
|
|
115
|
+
* 2nd Alternative :\S{1,3}( )
|
|
116
|
+
* : matches the character : literally
|
|
117
|
+
* \S{1,3} matches any non-whitespace character between 1 and 3 times, as many times as possible, giving back as needed (greedy)
|
|
118
|
+
* 4th Capturing Group ( )
|
|
119
|
+
*
|
|
120
|
+
* See https://regex101.com/r/HRS9O2/4
|
|
121
|
+
*/
|
|
122
|
+
_defineProperty(AsciiEmojiMatcher, "REGEX", new RegExp("((?:^|[\\s".concat(leafNodeReplacementCharacter, "])(?:\\(*?))(\\(?)([^:\\s").concat(leafNodeReplacementCharacter, "\\(]\\S{1,3}|:\\S{1,3}( ))$")));
|
|
123
|
+
var RecordingAsciiEmojiMatcher = /*#__PURE__*/function (_AsciiEmojiMatcher) {
|
|
124
|
+
_inherits(RecordingAsciiEmojiMatcher, _AsciiEmojiMatcher);
|
|
125
|
+
var _super = _createSuper(RecordingAsciiEmojiMatcher);
|
|
126
|
+
function RecordingAsciiEmojiMatcher(emojiProvider, asciiToEmojiMap) {
|
|
127
|
+
var _this;
|
|
128
|
+
_classCallCheck(this, RecordingAsciiEmojiMatcher);
|
|
129
|
+
_this = _super.call(this, asciiToEmojiMap);
|
|
130
|
+
_this.emojiProvider = emojiProvider;
|
|
131
|
+
return _this;
|
|
132
|
+
}
|
|
133
|
+
_createClass(RecordingAsciiEmojiMatcher, [{
|
|
134
|
+
key: "match",
|
|
135
|
+
value: function match(matchParts) {
|
|
136
|
+
var match = _get(_getPrototypeOf(RecordingAsciiEmojiMatcher.prototype), "match", this).call(this, matchParts);
|
|
137
|
+
if (match && this.emojiProvider.recordSelection) {
|
|
138
|
+
this.emojiProvider.recordSelection(match.emoji);
|
|
139
|
+
}
|
|
140
|
+
return match;
|
|
141
|
+
}
|
|
142
|
+
}]);
|
|
143
|
+
return RecordingAsciiEmojiMatcher;
|
|
144
|
+
}(AsciiEmojiMatcher);
|
|
145
|
+
var AsciiEmojiTransactionCreator = /*#__PURE__*/function () {
|
|
146
|
+
function AsciiEmojiTransactionCreator(state, match, start, end, editorAnalyticsAPI) {
|
|
147
|
+
_classCallCheck(this, AsciiEmojiTransactionCreator);
|
|
148
|
+
this.state = state;
|
|
149
|
+
this.match = match;
|
|
150
|
+
this.start = start;
|
|
151
|
+
this.end = end;
|
|
152
|
+
this.editorAnalyticsAPI = editorAnalyticsAPI;
|
|
153
|
+
}
|
|
154
|
+
_createClass(AsciiEmojiTransactionCreator, [{
|
|
155
|
+
key: "create",
|
|
156
|
+
value: function create() {
|
|
157
|
+
var _this$editorAnalytics;
|
|
158
|
+
var tr = this.state.tr.replaceWith(this.from, this.to, this.createNodes());
|
|
159
|
+
(_this$editorAnalytics = this.editorAnalyticsAPI) === null || _this$editorAnalytics === void 0 ? void 0 : _this$editorAnalytics.attachAnalyticsEvent({
|
|
160
|
+
action: ACTION.INSERTED,
|
|
161
|
+
actionSubject: ACTION_SUBJECT.DOCUMENT,
|
|
162
|
+
actionSubjectId: ACTION_SUBJECT_ID.EMOJI,
|
|
163
|
+
attributes: {
|
|
164
|
+
inputMethod: INPUT_METHOD.ASCII
|
|
165
|
+
},
|
|
166
|
+
eventType: EVENT_TYPE.TRACK
|
|
167
|
+
})(tr);
|
|
168
|
+
return tr;
|
|
169
|
+
}
|
|
170
|
+
}, {
|
|
171
|
+
key: "from",
|
|
172
|
+
get: function get() {
|
|
173
|
+
return this.start + this.match.leadingString.length;
|
|
174
|
+
}
|
|
175
|
+
}, {
|
|
176
|
+
key: "to",
|
|
177
|
+
get: function get() {
|
|
178
|
+
return this.end;
|
|
179
|
+
}
|
|
180
|
+
}, {
|
|
181
|
+
key: "createNodes",
|
|
182
|
+
value: function createNodes() {
|
|
183
|
+
var nodes = [this.createEmojiNode()];
|
|
184
|
+
if (this.trailingTextNodeRequired()) {
|
|
185
|
+
nodes.push(this.createTrailingTextNode());
|
|
186
|
+
}
|
|
187
|
+
return nodes;
|
|
188
|
+
}
|
|
189
|
+
}, {
|
|
190
|
+
key: "createEmojiNode",
|
|
191
|
+
value: function createEmojiNode() {
|
|
192
|
+
var emojiTypeNode = this.state.schema.nodes.emoji;
|
|
193
|
+
return emojiTypeNode.create(this.getEmojiNodeAttrs());
|
|
194
|
+
}
|
|
195
|
+
}, {
|
|
196
|
+
key: "getEmojiNodeAttrs",
|
|
197
|
+
value: function getEmojiNodeAttrs() {
|
|
198
|
+
var emoji = this.match.emoji;
|
|
199
|
+
return {
|
|
200
|
+
id: emoji.id,
|
|
201
|
+
shortName: emoji.shortName,
|
|
202
|
+
text: emoji.fallback || emoji.shortName
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
}, {
|
|
206
|
+
key: "trailingTextNodeRequired",
|
|
207
|
+
value: function trailingTextNodeRequired() {
|
|
208
|
+
return this.match.trailingString.length > 0;
|
|
209
|
+
}
|
|
210
|
+
}, {
|
|
211
|
+
key: "createTrailingTextNode",
|
|
212
|
+
value: function createTrailingTextNode() {
|
|
213
|
+
return this.state.schema.text(this.match.trailingString);
|
|
214
|
+
}
|
|
215
|
+
}]);
|
|
216
|
+
return AsciiEmojiTransactionCreator;
|
|
217
|
+
}();
|
|
218
|
+
export var stateKey = new PluginKey('asciiEmojiPlugin');
|
|
219
|
+
var plugins = function plugins(schema, providerFactory, featureFlags, editorAnalyticsAPI) {
|
|
220
|
+
return [inputRulePlugin(schema, providerFactory, featureFlags, editorAnalyticsAPI)].filter(function (plugin) {
|
|
221
|
+
return !!plugin;
|
|
222
|
+
});
|
|
223
|
+
};
|
|
224
|
+
export default plugins;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import _taggedTemplateLiteral from "@babel/runtime/helpers/taggedTemplateLiteral";
|
|
2
|
+
var _templateObject;
|
|
3
|
+
/** @jsx jsx */
|
|
4
|
+
import { css, jsx } from '@emotion/react';
|
|
5
|
+
import { Emoji } from '@atlaskit/editor-common/emoji';
|
|
6
|
+
// eslint-disable-next-line
|
|
7
|
+
var clickSelectWrapperStyle = css(_templateObject || (_templateObject = _taggedTemplateLiteral(["\n // eslint-disable-next-line @atlaskit/design-system/consistent-css-prop-usage\n user-select: all;\n"])));
|
|
8
|
+
export default function EmojiNode(props) {
|
|
9
|
+
return jsx("span", {
|
|
10
|
+
css: clickSelectWrapperStyle
|
|
11
|
+
}, jsx(Emoji, props));
|
|
12
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { EditorAnalyticsAPI, INPUT_METHOD } from '@atlaskit/editor-common/analytics';
|
|
2
|
+
import type { EditorCommand } from '@atlaskit/editor-common/types';
|
|
3
|
+
import type { EmojiId } from '@atlaskit/emoji';
|
|
4
|
+
export declare const insertEmoji: (editorAnalyticsAPI: EditorAnalyticsAPI | undefined) => (emojiId: EmojiId, inputMethod?: INPUT_METHOD.PICKER | INPUT_METHOD.ASCII | INPUT_METHOD.TYPEAHEAD) => EditorCommand;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import type { ProviderFactory } from '@atlaskit/editor-common/provider-factory';
|
|
3
|
+
import type { InlineNodeViewComponentProps } from '@atlaskit/editor-common/react-node-view';
|
|
4
|
+
export type Props = InlineNodeViewComponentProps & {
|
|
5
|
+
providerFactory: ProviderFactory;
|
|
6
|
+
};
|
|
7
|
+
export declare function EmojiNodeView(props: Props): JSX.Element;
|