@atlaskit/editor-plugin-emoji 13.1.11 → 13.3.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 +24 -0
- package/dist/cjs/nodeviews/EmojiNodeView.js +108 -6
- package/dist/es2019/nodeviews/EmojiNodeView.js +98 -1
- package/dist/esm/nodeviews/EmojiNodeView.js +108 -6
- package/dist/types/nodeviews/EmojiNodeView.d.ts +11 -3
- package/dist/types/pm-plugins/providers/EmojiNodeDataProvider.d.ts +1 -1
- package/package.json +16 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-emoji
|
|
2
2
|
|
|
3
|
+
## 13.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`51c33ef5349b6`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/51c33ef5349b6) -
|
|
8
|
+
Enable compatibility with React 19.2.0
|
|
9
|
+
|
|
10
|
+
### Patch Changes
|
|
11
|
+
|
|
12
|
+
- Updated dependencies
|
|
13
|
+
|
|
14
|
+
## 13.2.0
|
|
15
|
+
|
|
16
|
+
### Minor Changes
|
|
17
|
+
|
|
18
|
+
- [`a86fac88f3a5e`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/a86fac88f3a5e) -
|
|
19
|
+
Use lazy-loaded VanillaTooltip for emoji tooltips in the editor and render an ADS Tooltip
|
|
20
|
+
positioned above the emoji in the emoji element, gated behind the
|
|
21
|
+
platform_editor_emoji_hover_show_tooltip experiment.
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- Updated dependencies
|
|
26
|
+
|
|
3
27
|
## 13.1.11
|
|
4
28
|
|
|
5
29
|
### Patch Changes
|
|
@@ -11,11 +11,14 @@ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/
|
|
|
11
11
|
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
|
|
12
12
|
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
|
13
13
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
14
|
+
var _bindEventListener = require("bind-event-listener");
|
|
14
15
|
var _isEqual = _interopRequireDefault(require("lodash/isEqual"));
|
|
16
|
+
var _uniqueId = _interopRequireDefault(require("lodash/uniqueId"));
|
|
15
17
|
var _browserApis = require("@atlaskit/browser-apis");
|
|
16
18
|
var _coreUtils = require("@atlaskit/editor-common/core-utils");
|
|
17
19
|
var _emoji = require("@atlaskit/editor-common/emoji");
|
|
18
20
|
var _monitoring = require("@atlaskit/editor-common/monitoring");
|
|
21
|
+
var _vanillaTooltip = require("@atlaskit/editor-common/vanilla-tooltip");
|
|
19
22
|
var _editorPluginConnectivity = require("@atlaskit/editor-plugin-connectivity");
|
|
20
23
|
var _model = require("@atlaskit/editor-prosemirror/model");
|
|
21
24
|
var _emojiIdToEmoji = require("@atlaskit/emoji/emoji-id-to-emoji");
|
|
@@ -43,6 +46,7 @@ function isSingleEmoji(fallbackText) {
|
|
|
43
46
|
/**
|
|
44
47
|
* Emoji node view for renderering emoji nodes
|
|
45
48
|
*/
|
|
49
|
+
var EMOJI_TOOLTIP_CLASS = 'emoji-tooltip-editor';
|
|
46
50
|
var EmojiNodeView = exports.EmojiNodeView = /*#__PURE__*/function () {
|
|
47
51
|
/**
|
|
48
52
|
* Prosemirror node view for rendering emoji nodes. This class is responsible for
|
|
@@ -64,7 +68,11 @@ var EmojiNodeView = exports.EmojiNodeView = /*#__PURE__*/function () {
|
|
|
64
68
|
emojiNodeDataProvider = _ref.emojiNodeDataProvider;
|
|
65
69
|
(0, _classCallCheck2.default)(this, EmojiNodeView);
|
|
66
70
|
(0, _defineProperty2.default)(this, "renderingFallback", false);
|
|
67
|
-
(0, _defineProperty2.default)(this, "destroy", function () {
|
|
71
|
+
(0, _defineProperty2.default)(this, "destroy", function () {
|
|
72
|
+
if (_this.tooltipInstance || _this.tooltipTarget || _this.destroyLazyTooltipListeners) {
|
|
73
|
+
_this.destroyTooltip();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
68
76
|
this.node = node;
|
|
69
77
|
this.intl = intl;
|
|
70
78
|
var _DOMSerializer$render = _model.DOMSerializer.renderSpec(document, (0, _emojiNodeSpec.emojiToDom)(this.node)),
|
|
@@ -138,12 +146,25 @@ var EmojiNodeView = exports.EmojiNodeView = /*#__PURE__*/function () {
|
|
|
138
146
|
this.destroy = function () {
|
|
139
147
|
unsubscribe();
|
|
140
148
|
subscribeToConnection === null || subscribeToConnection === void 0 || subscribeToConnection();
|
|
149
|
+
_this.destroyTooltip();
|
|
141
150
|
};
|
|
142
151
|
}
|
|
143
152
|
}
|
|
144
153
|
|
|
145
154
|
/** Type guard to check if a Node is an HTMLElement in a safe way. */
|
|
146
155
|
return (0, _createClass2.default)(EmojiNodeView, [{
|
|
156
|
+
key: "destroyTooltip",
|
|
157
|
+
value: function destroyTooltip() {
|
|
158
|
+
var _this$destroyLazyTool, _this$tooltipInstance, _this$tooltipTarget, _this$tooltipTarget2;
|
|
159
|
+
(_this$destroyLazyTool = this.destroyLazyTooltipListeners) === null || _this$destroyLazyTool === void 0 || _this$destroyLazyTool.call(this);
|
|
160
|
+
(_this$tooltipInstance = this.tooltipInstance) === null || _this$tooltipInstance === void 0 || _this$tooltipInstance.destroy();
|
|
161
|
+
(_this$tooltipTarget = this.tooltipTarget) === null || _this$tooltipTarget === void 0 || _this$tooltipTarget.removeAttribute('popovertarget');
|
|
162
|
+
(_this$tooltipTarget2 = this.tooltipTarget) === null || _this$tooltipTarget2 === void 0 || _this$tooltipTarget2.removeAttribute('aria-describedby');
|
|
163
|
+
this.destroyLazyTooltipListeners = undefined;
|
|
164
|
+
this.tooltipInstance = undefined;
|
|
165
|
+
this.tooltipTarget = undefined;
|
|
166
|
+
}
|
|
167
|
+
}, {
|
|
147
168
|
key: "isHTMLElement",
|
|
148
169
|
value: function isHTMLElement(element) {
|
|
149
170
|
if (element === null) {
|
|
@@ -216,6 +237,8 @@ var EmojiNodeView = exports.EmojiNodeView = /*#__PURE__*/function () {
|
|
|
216
237
|
// Pay attention, this method should be called only when the emoji provider returns
|
|
217
238
|
// emoji data to prevent rendering empty emoji during loading.
|
|
218
239
|
function cleanUpAndRenderCommonAttributes() {
|
|
240
|
+
this.destroyTooltip();
|
|
241
|
+
|
|
219
242
|
// Clean up the DOM before rendering the new emoji
|
|
220
243
|
if (this.domElement) {
|
|
221
244
|
this.domElement.innerHTML = '';
|
|
@@ -225,6 +248,83 @@ var EmojiNodeView = exports.EmojiNodeView = /*#__PURE__*/function () {
|
|
|
225
248
|
this.domElement.removeAttribute('aria-busy');
|
|
226
249
|
}
|
|
227
250
|
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Lazily creates a VanillaTooltip on the given element showing the emoji shortName.
|
|
254
|
+
* Gated behind the platform_editor_emoji_hover_show_tooltip experiment.
|
|
255
|
+
* When the tooltip is active, the native `title` attribute is removed to avoid
|
|
256
|
+
* showing both the browser tooltip and the custom tooltip.
|
|
257
|
+
*/
|
|
258
|
+
}, {
|
|
259
|
+
key: "createTooltip",
|
|
260
|
+
value: function createTooltip(element, shortName) {
|
|
261
|
+
var _this2 = this;
|
|
262
|
+
if (!(0, _expValEquals.expValEquals)('platform_editor_emoji_hover_show_tooltip', 'isEnabled', true)) {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
if ((0, _coreUtils.isSSR)()) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
var initTooltip = function initTooltip(event) {
|
|
269
|
+
var _this2$destroyLazyToo;
|
|
270
|
+
(_this2$destroyLazyToo = _this2.destroyLazyTooltipListeners) === null || _this2$destroyLazyToo === void 0 || _this2$destroyLazyToo.call(_this2);
|
|
271
|
+
_this2.destroyLazyTooltipListeners = undefined;
|
|
272
|
+
var tooltipId = typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function' ? "emoji-tooltip-".concat(crypto.randomUUID()) : (0, _uniqueId.default)('emoji-tooltip-');
|
|
273
|
+
try {
|
|
274
|
+
var tooltipInstance = new _vanillaTooltip.VanillaTooltip(
|
|
275
|
+
// VanillaTooltip types expect HTMLButtonElement but works with any HTMLElement
|
|
276
|
+
element, shortName, tooltipId, EMOJI_TOOLTIP_CLASS,
|
|
277
|
+
// default timeout
|
|
278
|
+
300,
|
|
279
|
+
// Inline styles are required because the Popover API promotes the tooltip
|
|
280
|
+
// to the browser's top layer, where ancestor CSS selectors cannot reach it.
|
|
281
|
+
{
|
|
282
|
+
boxSizing: 'border-box',
|
|
283
|
+
maxWidth: '240px',
|
|
284
|
+
backgroundColor: "var(--ds-background-neutral-bold, #292A2E)",
|
|
285
|
+
border: 'none',
|
|
286
|
+
borderRadius: "var(--ds-radius-small, 3px)",
|
|
287
|
+
color: "var(--ds-text-inverse, #FFFFFF)",
|
|
288
|
+
font: "var(--ds-font-body-small, normal 400 12px/16px \"Atlassian Sans\", ui-sans-serif, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Ubuntu, \"Helvetica Neue\", sans-serif)",
|
|
289
|
+
fontFamily: "var(--ds-font-family-body, \"Atlassian Sans\", ui-sans-serif, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Ubuntu, \"Helvetica Neue\", sans-serif)",
|
|
290
|
+
insetBlockStart: "var(--ds-space-0, 0px)",
|
|
291
|
+
insetInlineStart: "var(--ds-space-0, 0px)",
|
|
292
|
+
overflowWrap: 'break-word',
|
|
293
|
+
paddingBlockStart: "var(--ds-space-050, 4px)",
|
|
294
|
+
paddingBlockEnd: "var(--ds-space-050, 4px)",
|
|
295
|
+
paddingInlineEnd: "var(--ds-space-075, 6px)",
|
|
296
|
+
paddingInlineStart: "var(--ds-space-075, 6px)",
|
|
297
|
+
whiteSpace: 'normal'
|
|
298
|
+
});
|
|
299
|
+
_this2.tooltipInstance = tooltipInstance;
|
|
300
|
+
_this2.tooltipTarget = element;
|
|
301
|
+
element.removeAttribute('title');
|
|
302
|
+
element.dispatchEvent(new Event(event.type));
|
|
303
|
+
} catch (error) {
|
|
304
|
+
element.removeAttribute('popovertarget');
|
|
305
|
+
element.removeAttribute('aria-describedby');
|
|
306
|
+
EmojiNodeView.logError(error instanceof Error ? error : new Error(String(error)));
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
var unbindMouseEnter = (0, _bindEventListener.bind)(element, {
|
|
310
|
+
type: 'mouseenter',
|
|
311
|
+
listener: initTooltip,
|
|
312
|
+
options: {
|
|
313
|
+
once: true
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
var unbindFocus = (0, _bindEventListener.bind)(element, {
|
|
317
|
+
type: 'focus',
|
|
318
|
+
listener: initTooltip,
|
|
319
|
+
options: {
|
|
320
|
+
once: true
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
this.destroyLazyTooltipListeners = function () {
|
|
324
|
+
unbindMouseEnter();
|
|
325
|
+
unbindFocus();
|
|
326
|
+
};
|
|
327
|
+
}
|
|
228
328
|
}, {
|
|
229
329
|
key: "renderFallback",
|
|
230
330
|
value: function renderFallback() {
|
|
@@ -254,6 +354,7 @@ var EmojiNodeView = exports.EmojiNodeView = /*#__PURE__*/function () {
|
|
|
254
354
|
fallbackElement.setAttribute('data-testid', "fallback-emoji-".concat(shortName));
|
|
255
355
|
fallbackElement.setAttribute('data-emoji-type', 'fallback');
|
|
256
356
|
this.dom.appendChild(fallbackElement);
|
|
357
|
+
this.createTooltip(fallbackElement, shortName);
|
|
257
358
|
}
|
|
258
359
|
}, {
|
|
259
360
|
key: "renderEmoji",
|
|
@@ -281,6 +382,7 @@ var EmojiNodeView = exports.EmojiNodeView = /*#__PURE__*/function () {
|
|
|
281
382
|
if (EmojiNodeView.shouldRecordUnicodeEmojiExposure(description, representation)) {
|
|
282
383
|
(0, _expValEquals.expValEquals)('platform_use_unicode_emojis', 'isEnabled', true);
|
|
283
384
|
}
|
|
385
|
+
this.createTooltip(containerElement, description.shortName);
|
|
284
386
|
}
|
|
285
387
|
}, {
|
|
286
388
|
key: "createUnicodeEmojiElement",
|
|
@@ -330,7 +432,7 @@ var EmojiNodeView = exports.EmojiNodeView = /*#__PURE__*/function () {
|
|
|
330
432
|
}, {
|
|
331
433
|
key: "createImageEmojiElement",
|
|
332
434
|
value: function createImageEmojiElement(emojiDescription, representation) {
|
|
333
|
-
var
|
|
435
|
+
var _this3 = this;
|
|
334
436
|
var doc = (0, _browserApis.getDocument)();
|
|
335
437
|
if (!doc) {
|
|
336
438
|
// eslint-disable-next-line @atlaskit/platform/no-direct-document-usage
|
|
@@ -349,13 +451,13 @@ var EmojiNodeView = exports.EmojiNodeView = /*#__PURE__*/function () {
|
|
|
349
451
|
// If there's an error (ie. offline) render the ascii fallback if possible, otherwise
|
|
350
452
|
// mark the node to refresh when returning online.
|
|
351
453
|
// Create a check that confirms if this.node.attrs.text if an ascii emoji
|
|
352
|
-
if (isSingleEmoji(
|
|
353
|
-
|
|
454
|
+
if (isSingleEmoji(_this3.node.attrs.text)) {
|
|
455
|
+
_this3.renderFallback();
|
|
354
456
|
} else {
|
|
355
|
-
|
|
457
|
+
_this3.renderingFallback = true;
|
|
356
458
|
}
|
|
357
459
|
} else {
|
|
358
|
-
|
|
460
|
+
_this3.renderFallback();
|
|
359
461
|
}
|
|
360
462
|
};
|
|
361
463
|
return imageElement;
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import { bind } from 'bind-event-listener';
|
|
2
3
|
import isEqual from 'lodash/isEqual';
|
|
4
|
+
import uniqueId from 'lodash/uniqueId';
|
|
3
5
|
import { getDocument } from '@atlaskit/browser-apis';
|
|
4
6
|
import { isSSR } from '@atlaskit/editor-common/core-utils';
|
|
5
7
|
import { messages, EmojiSharedCssClassName, defaultEmojiHeight } from '@atlaskit/editor-common/emoji';
|
|
6
8
|
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
9
|
+
import { VanillaTooltip } from '@atlaskit/editor-common/vanilla-tooltip';
|
|
7
10
|
import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
|
|
8
11
|
import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
|
|
9
12
|
import { emojiIdToEmoji } from '@atlaskit/emoji/emoji-id-to-emoji';
|
|
@@ -31,7 +34,18 @@ export function isSingleEmoji(fallbackText) {
|
|
|
31
34
|
/**
|
|
32
35
|
* Emoji node view for renderering emoji nodes
|
|
33
36
|
*/
|
|
37
|
+
const EMOJI_TOOLTIP_CLASS = 'emoji-tooltip-editor';
|
|
34
38
|
export class EmojiNodeView {
|
|
39
|
+
destroyTooltip() {
|
|
40
|
+
var _this$destroyLazyTool, _this$tooltipInstance, _this$tooltipTarget, _this$tooltipTarget2;
|
|
41
|
+
(_this$destroyLazyTool = this.destroyLazyTooltipListeners) === null || _this$destroyLazyTool === void 0 ? void 0 : _this$destroyLazyTool.call(this);
|
|
42
|
+
(_this$tooltipInstance = this.tooltipInstance) === null || _this$tooltipInstance === void 0 ? void 0 : _this$tooltipInstance.destroy();
|
|
43
|
+
(_this$tooltipTarget = this.tooltipTarget) === null || _this$tooltipTarget === void 0 ? void 0 : _this$tooltipTarget.removeAttribute('popovertarget');
|
|
44
|
+
(_this$tooltipTarget2 = this.tooltipTarget) === null || _this$tooltipTarget2 === void 0 ? void 0 : _this$tooltipTarget2.removeAttribute('aria-describedby');
|
|
45
|
+
this.destroyLazyTooltipListeners = undefined;
|
|
46
|
+
this.tooltipInstance = undefined;
|
|
47
|
+
this.tooltipTarget = undefined;
|
|
48
|
+
}
|
|
35
49
|
static logError(error) {
|
|
36
50
|
void logException(error, {
|
|
37
51
|
location: 'editor-plugin-emoji/EmojiNodeView'
|
|
@@ -57,7 +71,11 @@ export class EmojiNodeView {
|
|
|
57
71
|
emojiNodeDataProvider
|
|
58
72
|
}) {
|
|
59
73
|
_defineProperty(this, "renderingFallback", false);
|
|
60
|
-
_defineProperty(this, "destroy", () => {
|
|
74
|
+
_defineProperty(this, "destroy", () => {
|
|
75
|
+
if (this.tooltipInstance || this.tooltipTarget || this.destroyLazyTooltipListeners) {
|
|
76
|
+
this.destroyTooltip();
|
|
77
|
+
}
|
|
78
|
+
});
|
|
61
79
|
this.node = node;
|
|
62
80
|
this.intl = intl;
|
|
63
81
|
const {
|
|
@@ -134,6 +152,7 @@ export class EmojiNodeView {
|
|
|
134
152
|
this.destroy = () => {
|
|
135
153
|
unsubscribe();
|
|
136
154
|
subscribeToConnection === null || subscribeToConnection === void 0 ? void 0 : subscribeToConnection();
|
|
155
|
+
this.destroyTooltip();
|
|
137
156
|
};
|
|
138
157
|
}
|
|
139
158
|
}
|
|
@@ -189,6 +208,8 @@ export class EmojiNodeView {
|
|
|
189
208
|
// Pay attention, this method should be called only when the emoji provider returns
|
|
190
209
|
// emoji data to prevent rendering empty emoji during loading.
|
|
191
210
|
cleanUpAndRenderCommonAttributes() {
|
|
211
|
+
this.destroyTooltip();
|
|
212
|
+
|
|
192
213
|
// Clean up the DOM before rendering the new emoji
|
|
193
214
|
if (this.domElement) {
|
|
194
215
|
this.domElement.innerHTML = '';
|
|
@@ -198,6 +219,80 @@ export class EmojiNodeView {
|
|
|
198
219
|
this.domElement.removeAttribute('aria-busy');
|
|
199
220
|
}
|
|
200
221
|
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Lazily creates a VanillaTooltip on the given element showing the emoji shortName.
|
|
225
|
+
* Gated behind the platform_editor_emoji_hover_show_tooltip experiment.
|
|
226
|
+
* When the tooltip is active, the native `title` attribute is removed to avoid
|
|
227
|
+
* showing both the browser tooltip and the custom tooltip.
|
|
228
|
+
*/
|
|
229
|
+
createTooltip(element, shortName) {
|
|
230
|
+
if (!expValEquals('platform_editor_emoji_hover_show_tooltip', 'isEnabled', true)) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (isSSR()) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const initTooltip = event => {
|
|
237
|
+
var _this$destroyLazyTool2;
|
|
238
|
+
(_this$destroyLazyTool2 = this.destroyLazyTooltipListeners) === null || _this$destroyLazyTool2 === void 0 ? void 0 : _this$destroyLazyTool2.call(this);
|
|
239
|
+
this.destroyLazyTooltipListeners = undefined;
|
|
240
|
+
const tooltipId = typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function' ? `emoji-tooltip-${crypto.randomUUID()}` : uniqueId('emoji-tooltip-');
|
|
241
|
+
try {
|
|
242
|
+
const tooltipInstance = new VanillaTooltip(
|
|
243
|
+
// VanillaTooltip types expect HTMLButtonElement but works with any HTMLElement
|
|
244
|
+
element, shortName, tooltipId, EMOJI_TOOLTIP_CLASS,
|
|
245
|
+
// default timeout
|
|
246
|
+
300,
|
|
247
|
+
// Inline styles are required because the Popover API promotes the tooltip
|
|
248
|
+
// to the browser's top layer, where ancestor CSS selectors cannot reach it.
|
|
249
|
+
{
|
|
250
|
+
boxSizing: 'border-box',
|
|
251
|
+
maxWidth: '240px',
|
|
252
|
+
backgroundColor: "var(--ds-background-neutral-bold, #292A2E)",
|
|
253
|
+
border: 'none',
|
|
254
|
+
borderRadius: "var(--ds-radius-small, 3px)",
|
|
255
|
+
color: "var(--ds-text-inverse, #FFFFFF)",
|
|
256
|
+
font: "var(--ds-font-body-small, normal 400 12px/16px \"Atlassian Sans\", ui-sans-serif, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Ubuntu, \"Helvetica Neue\", sans-serif)",
|
|
257
|
+
fontFamily: "var(--ds-font-family-body, \"Atlassian Sans\", ui-sans-serif, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Ubuntu, \"Helvetica Neue\", sans-serif)",
|
|
258
|
+
insetBlockStart: "var(--ds-space-0, 0px)",
|
|
259
|
+
insetInlineStart: "var(--ds-space-0, 0px)",
|
|
260
|
+
overflowWrap: 'break-word',
|
|
261
|
+
paddingBlockStart: "var(--ds-space-050, 4px)",
|
|
262
|
+
paddingBlockEnd: "var(--ds-space-050, 4px)",
|
|
263
|
+
paddingInlineEnd: "var(--ds-space-075, 6px)",
|
|
264
|
+
paddingInlineStart: "var(--ds-space-075, 6px)",
|
|
265
|
+
whiteSpace: 'normal'
|
|
266
|
+
});
|
|
267
|
+
this.tooltipInstance = tooltipInstance;
|
|
268
|
+
this.tooltipTarget = element;
|
|
269
|
+
element.removeAttribute('title');
|
|
270
|
+
element.dispatchEvent(new Event(event.type));
|
|
271
|
+
} catch (error) {
|
|
272
|
+
element.removeAttribute('popovertarget');
|
|
273
|
+
element.removeAttribute('aria-describedby');
|
|
274
|
+
EmojiNodeView.logError(error instanceof Error ? error : new Error(String(error)));
|
|
275
|
+
}
|
|
276
|
+
};
|
|
277
|
+
const unbindMouseEnter = bind(element, {
|
|
278
|
+
type: 'mouseenter',
|
|
279
|
+
listener: initTooltip,
|
|
280
|
+
options: {
|
|
281
|
+
once: true
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
const unbindFocus = bind(element, {
|
|
285
|
+
type: 'focus',
|
|
286
|
+
listener: initTooltip,
|
|
287
|
+
options: {
|
|
288
|
+
once: true
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
this.destroyLazyTooltipListeners = () => {
|
|
292
|
+
unbindMouseEnter();
|
|
293
|
+
unbindFocus();
|
|
294
|
+
};
|
|
295
|
+
}
|
|
201
296
|
renderFallback() {
|
|
202
297
|
this.renderingFallback = true;
|
|
203
298
|
this.cleanUpAndRenderCommonAttributes();
|
|
@@ -226,6 +321,7 @@ export class EmojiNodeView {
|
|
|
226
321
|
fallbackElement.setAttribute('data-testid', `fallback-emoji-${shortName}`);
|
|
227
322
|
fallbackElement.setAttribute('data-emoji-type', 'fallback');
|
|
228
323
|
this.dom.appendChild(fallbackElement);
|
|
324
|
+
this.createTooltip(fallbackElement, shortName);
|
|
229
325
|
}
|
|
230
326
|
renderEmoji(description, representation) {
|
|
231
327
|
this.renderingFallback = false;
|
|
@@ -251,6 +347,7 @@ export class EmojiNodeView {
|
|
|
251
347
|
if (EmojiNodeView.shouldRecordUnicodeEmojiExposure(description, representation)) {
|
|
252
348
|
expValEquals('platform_use_unicode_emojis', 'isEnabled', true);
|
|
253
349
|
}
|
|
350
|
+
this.createTooltip(containerElement, description.shortName);
|
|
254
351
|
}
|
|
255
352
|
createUnicodeEmojiElement(emoji) {
|
|
256
353
|
let doc = getDocument();
|
|
@@ -3,11 +3,14 @@ import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
|
|
3
3
|
import _createClass from "@babel/runtime/helpers/createClass";
|
|
4
4
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
5
5
|
import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
6
|
+
import { bind } from 'bind-event-listener';
|
|
6
7
|
import isEqual from 'lodash/isEqual';
|
|
8
|
+
import uniqueId from 'lodash/uniqueId';
|
|
7
9
|
import { getDocument } from '@atlaskit/browser-apis';
|
|
8
10
|
import { isSSR } from '@atlaskit/editor-common/core-utils';
|
|
9
11
|
import { messages, EmojiSharedCssClassName, defaultEmojiHeight } from '@atlaskit/editor-common/emoji';
|
|
10
12
|
import { logException } from '@atlaskit/editor-common/monitoring';
|
|
13
|
+
import { VanillaTooltip } from '@atlaskit/editor-common/vanilla-tooltip';
|
|
11
14
|
import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity';
|
|
12
15
|
import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
|
|
13
16
|
import { emojiIdToEmoji } from '@atlaskit/emoji/emoji-id-to-emoji';
|
|
@@ -35,6 +38,7 @@ export function isSingleEmoji(fallbackText) {
|
|
|
35
38
|
/**
|
|
36
39
|
* Emoji node view for renderering emoji nodes
|
|
37
40
|
*/
|
|
41
|
+
var EMOJI_TOOLTIP_CLASS = 'emoji-tooltip-editor';
|
|
38
42
|
export var EmojiNodeView = /*#__PURE__*/function () {
|
|
39
43
|
/**
|
|
40
44
|
* Prosemirror node view for rendering emoji nodes. This class is responsible for
|
|
@@ -56,7 +60,11 @@ export var EmojiNodeView = /*#__PURE__*/function () {
|
|
|
56
60
|
emojiNodeDataProvider = _ref.emojiNodeDataProvider;
|
|
57
61
|
_classCallCheck(this, EmojiNodeView);
|
|
58
62
|
_defineProperty(this, "renderingFallback", false);
|
|
59
|
-
_defineProperty(this, "destroy", function () {
|
|
63
|
+
_defineProperty(this, "destroy", function () {
|
|
64
|
+
if (_this.tooltipInstance || _this.tooltipTarget || _this.destroyLazyTooltipListeners) {
|
|
65
|
+
_this.destroyTooltip();
|
|
66
|
+
}
|
|
67
|
+
});
|
|
60
68
|
this.node = node;
|
|
61
69
|
this.intl = intl;
|
|
62
70
|
var _DOMSerializer$render = DOMSerializer.renderSpec(document, emojiToDom(this.node)),
|
|
@@ -130,12 +138,25 @@ export var EmojiNodeView = /*#__PURE__*/function () {
|
|
|
130
138
|
this.destroy = function () {
|
|
131
139
|
unsubscribe();
|
|
132
140
|
subscribeToConnection === null || subscribeToConnection === void 0 || subscribeToConnection();
|
|
141
|
+
_this.destroyTooltip();
|
|
133
142
|
};
|
|
134
143
|
}
|
|
135
144
|
}
|
|
136
145
|
|
|
137
146
|
/** Type guard to check if a Node is an HTMLElement in a safe way. */
|
|
138
147
|
return _createClass(EmojiNodeView, [{
|
|
148
|
+
key: "destroyTooltip",
|
|
149
|
+
value: function destroyTooltip() {
|
|
150
|
+
var _this$destroyLazyTool, _this$tooltipInstance, _this$tooltipTarget, _this$tooltipTarget2;
|
|
151
|
+
(_this$destroyLazyTool = this.destroyLazyTooltipListeners) === null || _this$destroyLazyTool === void 0 || _this$destroyLazyTool.call(this);
|
|
152
|
+
(_this$tooltipInstance = this.tooltipInstance) === null || _this$tooltipInstance === void 0 || _this$tooltipInstance.destroy();
|
|
153
|
+
(_this$tooltipTarget = this.tooltipTarget) === null || _this$tooltipTarget === void 0 || _this$tooltipTarget.removeAttribute('popovertarget');
|
|
154
|
+
(_this$tooltipTarget2 = this.tooltipTarget) === null || _this$tooltipTarget2 === void 0 || _this$tooltipTarget2.removeAttribute('aria-describedby');
|
|
155
|
+
this.destroyLazyTooltipListeners = undefined;
|
|
156
|
+
this.tooltipInstance = undefined;
|
|
157
|
+
this.tooltipTarget = undefined;
|
|
158
|
+
}
|
|
159
|
+
}, {
|
|
139
160
|
key: "isHTMLElement",
|
|
140
161
|
value: function isHTMLElement(element) {
|
|
141
162
|
if (element === null) {
|
|
@@ -208,6 +229,8 @@ export var EmojiNodeView = /*#__PURE__*/function () {
|
|
|
208
229
|
// Pay attention, this method should be called only when the emoji provider returns
|
|
209
230
|
// emoji data to prevent rendering empty emoji during loading.
|
|
210
231
|
function cleanUpAndRenderCommonAttributes() {
|
|
232
|
+
this.destroyTooltip();
|
|
233
|
+
|
|
211
234
|
// Clean up the DOM before rendering the new emoji
|
|
212
235
|
if (this.domElement) {
|
|
213
236
|
this.domElement.innerHTML = '';
|
|
@@ -217,6 +240,83 @@ export var EmojiNodeView = /*#__PURE__*/function () {
|
|
|
217
240
|
this.domElement.removeAttribute('aria-busy');
|
|
218
241
|
}
|
|
219
242
|
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Lazily creates a VanillaTooltip on the given element showing the emoji shortName.
|
|
246
|
+
* Gated behind the platform_editor_emoji_hover_show_tooltip experiment.
|
|
247
|
+
* When the tooltip is active, the native `title` attribute is removed to avoid
|
|
248
|
+
* showing both the browser tooltip and the custom tooltip.
|
|
249
|
+
*/
|
|
250
|
+
}, {
|
|
251
|
+
key: "createTooltip",
|
|
252
|
+
value: function createTooltip(element, shortName) {
|
|
253
|
+
var _this2 = this;
|
|
254
|
+
if (!expValEquals('platform_editor_emoji_hover_show_tooltip', 'isEnabled', true)) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
if (isSSR()) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
var initTooltip = function initTooltip(event) {
|
|
261
|
+
var _this2$destroyLazyToo;
|
|
262
|
+
(_this2$destroyLazyToo = _this2.destroyLazyTooltipListeners) === null || _this2$destroyLazyToo === void 0 || _this2$destroyLazyToo.call(_this2);
|
|
263
|
+
_this2.destroyLazyTooltipListeners = undefined;
|
|
264
|
+
var tooltipId = typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function' ? "emoji-tooltip-".concat(crypto.randomUUID()) : uniqueId('emoji-tooltip-');
|
|
265
|
+
try {
|
|
266
|
+
var tooltipInstance = new VanillaTooltip(
|
|
267
|
+
// VanillaTooltip types expect HTMLButtonElement but works with any HTMLElement
|
|
268
|
+
element, shortName, tooltipId, EMOJI_TOOLTIP_CLASS,
|
|
269
|
+
// default timeout
|
|
270
|
+
300,
|
|
271
|
+
// Inline styles are required because the Popover API promotes the tooltip
|
|
272
|
+
// to the browser's top layer, where ancestor CSS selectors cannot reach it.
|
|
273
|
+
{
|
|
274
|
+
boxSizing: 'border-box',
|
|
275
|
+
maxWidth: '240px',
|
|
276
|
+
backgroundColor: "var(--ds-background-neutral-bold, #292A2E)",
|
|
277
|
+
border: 'none',
|
|
278
|
+
borderRadius: "var(--ds-radius-small, 3px)",
|
|
279
|
+
color: "var(--ds-text-inverse, #FFFFFF)",
|
|
280
|
+
font: "var(--ds-font-body-small, normal 400 12px/16px \"Atlassian Sans\", ui-sans-serif, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Ubuntu, \"Helvetica Neue\", sans-serif)",
|
|
281
|
+
fontFamily: "var(--ds-font-family-body, \"Atlassian Sans\", ui-sans-serif, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Ubuntu, \"Helvetica Neue\", sans-serif)",
|
|
282
|
+
insetBlockStart: "var(--ds-space-0, 0px)",
|
|
283
|
+
insetInlineStart: "var(--ds-space-0, 0px)",
|
|
284
|
+
overflowWrap: 'break-word',
|
|
285
|
+
paddingBlockStart: "var(--ds-space-050, 4px)",
|
|
286
|
+
paddingBlockEnd: "var(--ds-space-050, 4px)",
|
|
287
|
+
paddingInlineEnd: "var(--ds-space-075, 6px)",
|
|
288
|
+
paddingInlineStart: "var(--ds-space-075, 6px)",
|
|
289
|
+
whiteSpace: 'normal'
|
|
290
|
+
});
|
|
291
|
+
_this2.tooltipInstance = tooltipInstance;
|
|
292
|
+
_this2.tooltipTarget = element;
|
|
293
|
+
element.removeAttribute('title');
|
|
294
|
+
element.dispatchEvent(new Event(event.type));
|
|
295
|
+
} catch (error) {
|
|
296
|
+
element.removeAttribute('popovertarget');
|
|
297
|
+
element.removeAttribute('aria-describedby');
|
|
298
|
+
EmojiNodeView.logError(error instanceof Error ? error : new Error(String(error)));
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
var unbindMouseEnter = bind(element, {
|
|
302
|
+
type: 'mouseenter',
|
|
303
|
+
listener: initTooltip,
|
|
304
|
+
options: {
|
|
305
|
+
once: true
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
var unbindFocus = bind(element, {
|
|
309
|
+
type: 'focus',
|
|
310
|
+
listener: initTooltip,
|
|
311
|
+
options: {
|
|
312
|
+
once: true
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
this.destroyLazyTooltipListeners = function () {
|
|
316
|
+
unbindMouseEnter();
|
|
317
|
+
unbindFocus();
|
|
318
|
+
};
|
|
319
|
+
}
|
|
220
320
|
}, {
|
|
221
321
|
key: "renderFallback",
|
|
222
322
|
value: function renderFallback() {
|
|
@@ -246,6 +346,7 @@ export var EmojiNodeView = /*#__PURE__*/function () {
|
|
|
246
346
|
fallbackElement.setAttribute('data-testid', "fallback-emoji-".concat(shortName));
|
|
247
347
|
fallbackElement.setAttribute('data-emoji-type', 'fallback');
|
|
248
348
|
this.dom.appendChild(fallbackElement);
|
|
349
|
+
this.createTooltip(fallbackElement, shortName);
|
|
249
350
|
}
|
|
250
351
|
}, {
|
|
251
352
|
key: "renderEmoji",
|
|
@@ -273,6 +374,7 @@ export var EmojiNodeView = /*#__PURE__*/function () {
|
|
|
273
374
|
if (EmojiNodeView.shouldRecordUnicodeEmojiExposure(description, representation)) {
|
|
274
375
|
expValEquals('platform_use_unicode_emojis', 'isEnabled', true);
|
|
275
376
|
}
|
|
377
|
+
this.createTooltip(containerElement, description.shortName);
|
|
276
378
|
}
|
|
277
379
|
}, {
|
|
278
380
|
key: "createUnicodeEmojiElement",
|
|
@@ -322,7 +424,7 @@ export var EmojiNodeView = /*#__PURE__*/function () {
|
|
|
322
424
|
}, {
|
|
323
425
|
key: "createImageEmojiElement",
|
|
324
426
|
value: function createImageEmojiElement(emojiDescription, representation) {
|
|
325
|
-
var
|
|
427
|
+
var _this3 = this;
|
|
326
428
|
var doc = getDocument();
|
|
327
429
|
if (!doc) {
|
|
328
430
|
// eslint-disable-next-line @atlaskit/platform/no-direct-document-usage
|
|
@@ -341,13 +443,13 @@ export var EmojiNodeView = /*#__PURE__*/function () {
|
|
|
341
443
|
// If there's an error (ie. offline) render the ascii fallback if possible, otherwise
|
|
342
444
|
// mark the node to refresh when returning online.
|
|
343
445
|
// Create a check that confirms if this.node.attrs.text if an ascii emoji
|
|
344
|
-
if (isSingleEmoji(
|
|
345
|
-
|
|
446
|
+
if (isSingleEmoji(_this3.node.attrs.text)) {
|
|
447
|
+
_this3.renderFallback();
|
|
346
448
|
} else {
|
|
347
|
-
|
|
449
|
+
_this3.renderingFallback = true;
|
|
348
450
|
}
|
|
349
451
|
} else {
|
|
350
|
-
|
|
452
|
+
_this3.renderFallback();
|
|
351
453
|
}
|
|
352
454
|
};
|
|
353
455
|
return imageElement;
|
|
@@ -18,16 +18,17 @@ interface Params {
|
|
|
18
18
|
* isSingleEmoji('😀') // true
|
|
19
19
|
*/
|
|
20
20
|
export declare function isSingleEmoji(fallbackText: string): boolean;
|
|
21
|
-
/**
|
|
22
|
-
* Emoji node view for renderering emoji nodes
|
|
23
|
-
*/
|
|
24
21
|
export declare class EmojiNodeView implements NodeView {
|
|
25
22
|
dom: Node;
|
|
26
23
|
domElement: HTMLElement | undefined;
|
|
27
24
|
private readonly node;
|
|
28
25
|
private readonly intl;
|
|
29
26
|
private renderingFallback;
|
|
27
|
+
private tooltipInstance;
|
|
28
|
+
private tooltipTarget;
|
|
29
|
+
private destroyLazyTooltipListeners;
|
|
30
30
|
readonly destroy: () => void;
|
|
31
|
+
private destroyTooltip;
|
|
31
32
|
private static logError;
|
|
32
33
|
/**
|
|
33
34
|
* Prosemirror node view for rendering emoji nodes. This class is responsible for
|
|
@@ -49,6 +50,13 @@ export declare class EmojiNodeView implements NodeView {
|
|
|
49
50
|
private static isEmojiRepresentationSupported;
|
|
50
51
|
private static shouldRecordUnicodeEmojiExposure;
|
|
51
52
|
private cleanUpAndRenderCommonAttributes;
|
|
53
|
+
/**
|
|
54
|
+
* Lazily creates a VanillaTooltip on the given element showing the emoji shortName.
|
|
55
|
+
* Gated behind the platform_editor_emoji_hover_show_tooltip experiment.
|
|
56
|
+
* When the tooltip is active, the native `title` attribute is removed to avoid
|
|
57
|
+
* showing both the browser tooltip and the custom tooltip.
|
|
58
|
+
*/
|
|
59
|
+
private createTooltip;
|
|
52
60
|
private renderFallback;
|
|
53
61
|
private renderEmoji;
|
|
54
62
|
private createUnicodeEmojiElement;
|
|
@@ -4,7 +4,7 @@ import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
|
4
4
|
import type { EmojiResource, OptionalEmojiDescriptionWithVariations } from '@atlaskit/emoji';
|
|
5
5
|
import { NodeDataProvider } from '@atlaskit/node-data-provider';
|
|
6
6
|
export declare class EmojiNodeDataProvider extends NodeDataProvider<EmojiDefinition, OptionalEmojiDescriptionWithVariations> {
|
|
7
|
-
name:
|
|
7
|
+
name: 'emojiNodeDataProvider';
|
|
8
8
|
private readonly emojiResource;
|
|
9
9
|
private readonly emojiProvider;
|
|
10
10
|
constructor(resource: EmojiResource);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-emoji",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.3.0",
|
|
4
4
|
"description": "Emoji plugin for @atlaskit/editor-core",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -21,39 +21,40 @@
|
|
|
21
21
|
"singleton": true
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@atlaskit/adf-schema": "^56.
|
|
24
|
+
"@atlaskit/adf-schema": "^56.2.0",
|
|
25
25
|
"@atlaskit/browser-apis": "^1.2.0",
|
|
26
|
-
"@atlaskit/editor-plugin-analytics": "^12.
|
|
27
|
-
"@atlaskit/editor-plugin-annotation": "^12.
|
|
26
|
+
"@atlaskit/editor-plugin-analytics": "^12.1.0",
|
|
27
|
+
"@atlaskit/editor-plugin-annotation": "^12.1.0",
|
|
28
28
|
"@atlaskit/editor-plugin-base": "^13.0.0",
|
|
29
|
-
"@atlaskit/editor-plugin-connectivity": "^12.
|
|
30
|
-
"@atlaskit/editor-plugin-editor-viewmode": "^14.
|
|
31
|
-
"@atlaskit/editor-plugin-metrics": "^13.
|
|
32
|
-
"@atlaskit/editor-plugin-type-ahead": "^13.
|
|
29
|
+
"@atlaskit/editor-plugin-connectivity": "^12.1.0",
|
|
30
|
+
"@atlaskit/editor-plugin-editor-viewmode": "^14.1.0",
|
|
31
|
+
"@atlaskit/editor-plugin-metrics": "^13.1.0",
|
|
32
|
+
"@atlaskit/editor-plugin-type-ahead": "^13.2.0",
|
|
33
33
|
"@atlaskit/editor-prosemirror": "^8.0.0",
|
|
34
34
|
"@atlaskit/editor-shared-styles": "^4.0.0",
|
|
35
|
-
"@atlaskit/emoji": "^71.
|
|
35
|
+
"@atlaskit/emoji": "^71.15.0",
|
|
36
36
|
"@atlaskit/icon": "^37.2.0",
|
|
37
37
|
"@atlaskit/node-data-provider": "^13.0.0",
|
|
38
38
|
"@atlaskit/platform-feature-flags": "^2.1.0",
|
|
39
39
|
"@atlaskit/prosemirror-input-rules": "^4.0.0",
|
|
40
|
-
"@atlaskit/tmp-editor-statsig": "^
|
|
40
|
+
"@atlaskit/tmp-editor-statsig": "^135.7.0",
|
|
41
41
|
"@atlaskit/tokens": "^16.3.0",
|
|
42
42
|
"@babel/runtime": "^7.0.0",
|
|
43
43
|
"@emotion/react": "^11.7.1",
|
|
44
|
+
"bind-event-listener": "^3.0.0",
|
|
44
45
|
"lodash": "^4.17.21",
|
|
45
46
|
"react-loadable": "^5.1.0"
|
|
46
47
|
},
|
|
47
48
|
"peerDependencies": {
|
|
48
|
-
"@atlaskit/editor-common": "^116.
|
|
49
|
-
"react": "^18.2.0",
|
|
50
|
-
"react-dom": "^18.2.0",
|
|
49
|
+
"@atlaskit/editor-common": "^116.45.0",
|
|
50
|
+
"react": "^18.2.0 || ^19.2.0",
|
|
51
|
+
"react-dom": "^18.2.0 || ^19.2.0",
|
|
51
52
|
"react-intl": "^5.25.1 || ^6.0.0 || ^7.0.0"
|
|
52
53
|
},
|
|
53
54
|
"devDependencies": {
|
|
54
55
|
"@testing-library/react": "^16.3.0",
|
|
55
|
-
"react": "^
|
|
56
|
-
"react-dom": "^
|
|
56
|
+
"react": "^19.2.0",
|
|
57
|
+
"react-dom": "^19.2.0",
|
|
57
58
|
"react-intl": "^7.0.0",
|
|
58
59
|
"wait-for-expect": "^1.2.0"
|
|
59
60
|
},
|