@atlaskit/editor-plugin-emoji 3.2.3 → 3.4.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 +26 -0
- package/dist/cjs/emojiPlugin.js +17 -9
- package/dist/cjs/nodeviews/EmojiNodeView.js +189 -0
- package/dist/cjs/nodeviews/emojiNodeSpec.js +36 -32
- package/dist/es2019/emojiPlugin.js +18 -10
- package/dist/es2019/nodeviews/EmojiNodeView.js +149 -0
- package/dist/es2019/nodeviews/emojiNodeSpec.js +36 -35
- package/dist/esm/emojiPlugin.js +18 -10
- package/dist/esm/nodeviews/EmojiNodeView.js +182 -0
- package/dist/esm/nodeviews/emojiNodeSpec.js +35 -32
- package/dist/types/nodeviews/EmojiNodeView.d.ts +23 -0
- package/dist/types/nodeviews/emojiNodeSpec.d.ts +2 -0
- package/dist/types-ts4.5/nodeviews/EmojiNodeView.d.ts +23 -0
- package/dist/types-ts4.5/nodeviews/emojiNodeSpec.d.ts +2 -0
- package/package.json +7 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# @atlaskit/editor-plugin-emoji
|
|
2
2
|
|
|
3
|
+
## 3.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#135822](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/135822)
|
|
8
|
+
[`020b063194651`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/020b063194651) -
|
|
9
|
+
[https://product-fabric.atlassian.net/browse/ED-27424](ED-27424) - add exposure point to
|
|
10
|
+
`platform_editor_vanilla_dom` Statsig experiment into `editor-plugin-emoji` plugin
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- Updated dependencies
|
|
15
|
+
|
|
16
|
+
## 3.3.0
|
|
17
|
+
|
|
18
|
+
### Minor Changes
|
|
19
|
+
|
|
20
|
+
- [#133547](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/pull-requests/133547)
|
|
21
|
+
[`d13f959c13041`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/d13f959c13041) -
|
|
22
|
+
[https://product-fabric.atlassian.net/browse/ED-27374](ED-27374) - rewrite editor-emoji-plugin in
|
|
23
|
+
vanilla JS
|
|
24
|
+
|
|
25
|
+
### Patch Changes
|
|
26
|
+
|
|
27
|
+
- Updated dependencies
|
|
28
|
+
|
|
3
29
|
## 3.2.3
|
|
4
30
|
|
|
5
31
|
### Patch Changes
|
package/dist/cjs/emojiPlugin.js
CHANGED
|
@@ -32,6 +32,7 @@ var _experiments = require("@atlaskit/tmp-editor-statsig/experiments");
|
|
|
32
32
|
var _insertEmoji = require("./editor-commands/insert-emoji");
|
|
33
33
|
var _emoji3 = require("./nodeviews/emoji");
|
|
34
34
|
var _emojiNodeSpec = require("./nodeviews/emojiNodeSpec");
|
|
35
|
+
var _EmojiNodeView = require("./nodeviews/EmojiNodeView");
|
|
35
36
|
var _actions = require("./pm-plugins/actions");
|
|
36
37
|
var _asciiInputRules = require("./pm-plugins/ascii-input-rules");
|
|
37
38
|
var _InlineEmojiPopup = require("./ui/InlineEmojiPopup");
|
|
@@ -372,7 +373,7 @@ var emojiPlugin = exports.emojiPlugin = function emojiPlugin(_ref2) {
|
|
|
372
373
|
var _api$editorViewMode;
|
|
373
374
|
return (api === null || api === void 0 || (_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 || (_api$editorViewMode = _api$editorViewMode.sharedState.currentState()) === null || _api$editorViewMode === void 0 ? void 0 : _api$editorViewMode.mode) === 'view';
|
|
374
375
|
};
|
|
375
|
-
if (!isViewMode()
|
|
376
|
+
if (!isViewMode()) {
|
|
376
377
|
return undefined;
|
|
377
378
|
}
|
|
378
379
|
var onClick = function onClick(stateFromClickEvent, dispatch) {
|
|
@@ -542,15 +543,22 @@ function createEmojiPlugin(pmPluginFactoryParams, options, api) {
|
|
|
542
543
|
},
|
|
543
544
|
props: {
|
|
544
545
|
nodeViews: {
|
|
545
|
-
emoji: (
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
options: options,
|
|
546
|
+
emoji: function emoji(node, view, getPos, decorations) {
|
|
547
|
+
return (0, _experiments.editorExperiment)('platform_editor_vanilla_dom', true, {
|
|
548
|
+
exposure: true
|
|
549
|
+
}) ? new _EmojiNodeView.EmojiNodeView(node, {
|
|
550
|
+
intl: pmPluginFactoryParams.getIntl(),
|
|
551
551
|
api: api
|
|
552
|
-
}
|
|
553
|
-
|
|
552
|
+
}) : (0, _reactNodeView.getInlineNodeViewProducer)({
|
|
553
|
+
pmPluginFactoryParams: pmPluginFactoryParams,
|
|
554
|
+
Component: _emoji3.EmojiNodeView,
|
|
555
|
+
extraComponentProps: {
|
|
556
|
+
providerFactory: pmPluginFactoryParams.providerFactory,
|
|
557
|
+
options: options,
|
|
558
|
+
api: api
|
|
559
|
+
}
|
|
560
|
+
})(node, view, getPos, decorations);
|
|
561
|
+
}
|
|
554
562
|
}
|
|
555
563
|
},
|
|
556
564
|
view: function view(editorView) {
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.EmojiNodeView = void 0;
|
|
8
|
+
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
|
|
9
|
+
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
|
|
10
|
+
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
|
|
11
|
+
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
|
12
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
13
|
+
var _emoji = require("@atlaskit/editor-common/emoji");
|
|
14
|
+
var _model = require("@atlaskit/editor-prosemirror/model");
|
|
15
|
+
var _emojiNodeSpec = require("./emojiNodeSpec");
|
|
16
|
+
var EmojiNodeView = exports.EmojiNodeView = /*#__PURE__*/function () {
|
|
17
|
+
function EmojiNodeView(node, _ref) {
|
|
18
|
+
var _this = this;
|
|
19
|
+
var intl = _ref.intl,
|
|
20
|
+
api = _ref.api;
|
|
21
|
+
(0, _classCallCheck2.default)(this, EmojiNodeView);
|
|
22
|
+
(0, _defineProperty2.default)(this, "destroy", function () {});
|
|
23
|
+
// The pure `span` element will be used as a worse fallback only
|
|
24
|
+
// if DOMSerializer.renderSpec() in constructor fails.
|
|
25
|
+
(0, _defineProperty2.default)(this, "dom", document.createElement('span'));
|
|
26
|
+
this.node = node;
|
|
27
|
+
this.intl = intl;
|
|
28
|
+
try {
|
|
29
|
+
var _api$emoji, _sharedState$currentS;
|
|
30
|
+
var _DOMSerializer$render = _model.DOMSerializer.renderSpec(document, (0, _emojiNodeSpec.emojiToDom)(this.node)),
|
|
31
|
+
dom = _DOMSerializer$render.dom;
|
|
32
|
+
if (!(dom instanceof HTMLElement)) {
|
|
33
|
+
// It's safe to throw error here because, the code is wrapped in try-catch.
|
|
34
|
+
// However, it should never happen because `DOMSerializer.renderSpec()` should always return HTMLElement.
|
|
35
|
+
throw new Error('DOMSerializer.renderSpec() did not return HTMLElement');
|
|
36
|
+
}
|
|
37
|
+
this.dom = dom;
|
|
38
|
+
|
|
39
|
+
// We use the `emojiProvider` from the shared state
|
|
40
|
+
// because it supports the `emojiProvider` prop in the `ComposableEditor` options
|
|
41
|
+
// as well as the `emojiProvider` in the `EmojiPlugin` options.
|
|
42
|
+
var sharedState = api === null || api === void 0 || (_api$emoji = api.emoji) === null || _api$emoji === void 0 ? void 0 : _api$emoji.sharedState;
|
|
43
|
+
if (!sharedState) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
var emojiProvider = (_sharedState$currentS = sharedState.currentState()) === null || _sharedState$currentS === void 0 ? void 0 : _sharedState$currentS.emojiProvider;
|
|
47
|
+
if (emojiProvider) {
|
|
48
|
+
void this.updateDom(emojiProvider);
|
|
49
|
+
}
|
|
50
|
+
var unsubscribe = sharedState.onChange(function (_ref2) {
|
|
51
|
+
var nextSharedState = _ref2.nextSharedState;
|
|
52
|
+
if (emojiProvider === (nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.emojiProvider)) {
|
|
53
|
+
// Do not update if the provider is the same
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
emojiProvider = nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.emojiProvider;
|
|
57
|
+
void _this.updateDom(emojiProvider);
|
|
58
|
+
});
|
|
59
|
+
this.destroy = function () {
|
|
60
|
+
unsubscribe();
|
|
61
|
+
};
|
|
62
|
+
} catch (error) {
|
|
63
|
+
// TODO: ED-27390 - send analytics event with error
|
|
64
|
+
|
|
65
|
+
this.renderFallback();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return (0, _createClass2.default)(EmojiNodeView, [{
|
|
69
|
+
key: "updateDom",
|
|
70
|
+
value: function () {
|
|
71
|
+
var _updateDom = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(emojiProvider) {
|
|
72
|
+
var _this$node$attrs, shortName, id, fallback, emojiDescription, emojiRepresentation;
|
|
73
|
+
return _regenerator.default.wrap(function _callee$(_context) {
|
|
74
|
+
while (1) switch (_context.prev = _context.next) {
|
|
75
|
+
case 0:
|
|
76
|
+
// Clean up the DOM before rendering the new emoji
|
|
77
|
+
this.dom.innerHTML = '';
|
|
78
|
+
this.dom.style.cssText = '';
|
|
79
|
+
this.dom.classList.remove(_emoji.EmojiSharedCssClassName.EMOJI_PLACEHOLDER);
|
|
80
|
+
this.dom.removeAttribute('aria-label'); // The label is set in the renderEmoji method
|
|
81
|
+
this.dom.removeAttribute('aria-busy');
|
|
82
|
+
|
|
83
|
+
// Each vanilla JS node implementation should have this data attribute
|
|
84
|
+
this.dom.setAttribute('data-prosemirror-node-view-type', 'vanilla');
|
|
85
|
+
_context.prev = 6;
|
|
86
|
+
_this$node$attrs = this.node.attrs, shortName = _this$node$attrs.shortName, id = _this$node$attrs.id, fallback = _this$node$attrs.text;
|
|
87
|
+
_context.next = 10;
|
|
88
|
+
return emojiProvider === null || emojiProvider === void 0 ? void 0 : emojiProvider.fetchByEmojiId({
|
|
89
|
+
id: id,
|
|
90
|
+
shortName: shortName,
|
|
91
|
+
fallback: fallback
|
|
92
|
+
}, true);
|
|
93
|
+
case 10:
|
|
94
|
+
emojiDescription = _context.sent;
|
|
95
|
+
emojiRepresentation = emojiDescription === null || emojiDescription === void 0 ? void 0 : emojiDescription.representation;
|
|
96
|
+
if (emojiDescription && this.isEmojiRepresentationSupported(emojiRepresentation)) {
|
|
97
|
+
this.renderEmoji(emojiDescription, emojiRepresentation);
|
|
98
|
+
} else {
|
|
99
|
+
// TODO: ED-27390 - send analytics event with info that emoji description is not supported
|
|
100
|
+
|
|
101
|
+
this.renderFallback();
|
|
102
|
+
}
|
|
103
|
+
_context.next = 18;
|
|
104
|
+
break;
|
|
105
|
+
case 15:
|
|
106
|
+
_context.prev = 15;
|
|
107
|
+
_context.t0 = _context["catch"](6);
|
|
108
|
+
// TODO: ED-27390 - send analytics event with error
|
|
109
|
+
|
|
110
|
+
this.renderFallback();
|
|
111
|
+
case 18:
|
|
112
|
+
case "end":
|
|
113
|
+
return _context.stop();
|
|
114
|
+
}
|
|
115
|
+
}, _callee, this, [[6, 15]]);
|
|
116
|
+
}));
|
|
117
|
+
function updateDom(_x) {
|
|
118
|
+
return _updateDom.apply(this, arguments);
|
|
119
|
+
}
|
|
120
|
+
return updateDom;
|
|
121
|
+
}()
|
|
122
|
+
}, {
|
|
123
|
+
key: "isEmojiRepresentationSupported",
|
|
124
|
+
value: function isEmojiRepresentationSupported(representation) {
|
|
125
|
+
return !!representation && ('sprite' in representation || 'imagePath' in representation || 'mediaPath' in representation);
|
|
126
|
+
}
|
|
127
|
+
}, {
|
|
128
|
+
key: "renderFallback",
|
|
129
|
+
value: function renderFallback() {
|
|
130
|
+
// TODO: ED-27390 - send analytics event with info that fallback was used
|
|
131
|
+
|
|
132
|
+
var fallbackElement = document.createElement('span');
|
|
133
|
+
fallbackElement.innerText = this.node.attrs.text || this.node.attrs.shortName;
|
|
134
|
+
fallbackElement.setAttribute('data-testid', "sprite-emoji-".concat(this.node.attrs.shortName));
|
|
135
|
+
fallbackElement.setAttribute('data-emoji-type', 'sprite');
|
|
136
|
+
this.dom.appendChild(fallbackElement);
|
|
137
|
+
}
|
|
138
|
+
}, {
|
|
139
|
+
key: "renderEmoji",
|
|
140
|
+
value: function renderEmoji(description, representation) {
|
|
141
|
+
// TODO: ED-27390 - Add necessary analytics UFO events
|
|
142
|
+
|
|
143
|
+
var emojiType = 'sprite' in representation ? 'sprite' : 'image';
|
|
144
|
+
|
|
145
|
+
// Add wrapper for the emoji
|
|
146
|
+
var containerElement = document.createElement('span');
|
|
147
|
+
containerElement.setAttribute('role', 'img');
|
|
148
|
+
containerElement.classList.add(_emoji.EmojiSharedCssClassName.EMOJI_CONTAINER);
|
|
149
|
+
containerElement.setAttribute('data-testid', "".concat(emojiType, "-emoji-").concat(description.shortName));
|
|
150
|
+
containerElement.setAttribute('data-emoji-type', emojiType);
|
|
151
|
+
containerElement.setAttribute('aria-label', "".concat(this.intl.formatMessage(_emoji.messages.emojiNodeLabel), " ").concat(description.shortName));
|
|
152
|
+
var emojiElement = 'sprite' in representation ? this.createSpriteEmojiElement(representation) : this.createImageEmojiElement(description, representation);
|
|
153
|
+
containerElement.appendChild(emojiElement);
|
|
154
|
+
this.dom.appendChild(containerElement);
|
|
155
|
+
}
|
|
156
|
+
}, {
|
|
157
|
+
key: "createSpriteEmojiElement",
|
|
158
|
+
value: function createSpriteEmojiElement(representation) {
|
|
159
|
+
var spriteElement = document.createElement('span');
|
|
160
|
+
spriteElement.classList.add(_emoji.EmojiSharedCssClassName.EMOJI_SPRITE);
|
|
161
|
+
var sprite = representation.sprite;
|
|
162
|
+
var xPositionInPercent = 100 / (sprite.column - 1) * representation.xIndex;
|
|
163
|
+
var yPositionInPercent = 100 / (sprite.row - 1) * representation.yIndex;
|
|
164
|
+
spriteElement.style.backgroundImage = "url(".concat(sprite.url, ")");
|
|
165
|
+
spriteElement.style.backgroundPosition = "".concat(xPositionInPercent, "% ").concat(yPositionInPercent, "%");
|
|
166
|
+
spriteElement.style.backgroundSize = "".concat(sprite.column * 100, "% ").concat(sprite.row * 100, "%");
|
|
167
|
+
spriteElement.style.width = "".concat(_emoji.defaultEmojiHeight, "px");
|
|
168
|
+
spriteElement.style.minWidth = "".concat(_emoji.defaultEmojiHeight, "px");
|
|
169
|
+
spriteElement.style.height = "".concat(_emoji.defaultEmojiHeight, "px");
|
|
170
|
+
spriteElement.style.minHeight = "".concat(_emoji.defaultEmojiHeight, "px");
|
|
171
|
+
return spriteElement;
|
|
172
|
+
}
|
|
173
|
+
}, {
|
|
174
|
+
key: "createImageEmojiElement",
|
|
175
|
+
value: function createImageEmojiElement(emojiDescription, representation) {
|
|
176
|
+
var imageElement = document.createElement('img');
|
|
177
|
+
imageElement.classList.add(_emoji.EmojiSharedCssClassName.EMOJI_IMAGE);
|
|
178
|
+
imageElement.src = 'imagePath' in representation ? representation.imagePath : representation.mediaPath;
|
|
179
|
+
imageElement.loading = 'lazy';
|
|
180
|
+
imageElement.alt = emojiDescription.name || emojiDescription.shortName;
|
|
181
|
+
if (representation.width && representation.height) {
|
|
182
|
+
imageElement.height = _emoji.defaultEmojiHeight;
|
|
183
|
+
// Because img.width is round to the nearest integer.
|
|
184
|
+
imageElement.setAttribute('width', "".concat(_emoji.defaultEmojiHeight / representation.height * representation.width));
|
|
185
|
+
}
|
|
186
|
+
return imageElement;
|
|
187
|
+
}
|
|
188
|
+
}]);
|
|
189
|
+
}();
|
|
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
7
|
exports.emojiNodeSpec = void 0;
|
|
8
|
+
exports.emojiToDom = emojiToDom;
|
|
8
9
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
10
|
var _adfSchema = require("@atlaskit/adf-schema");
|
|
10
11
|
var _lazyNodeView = require("@atlaskit/editor-common/lazy-node-view");
|
|
@@ -25,37 +26,40 @@ var emojiNodeSpec = exports.emojiNodeSpec = function emojiNodeSpec() {
|
|
|
25
26
|
}
|
|
26
27
|
return _objectSpread(_objectSpread({}, _adfSchema.emoji), {}, {
|
|
27
28
|
toDOM: function toDOM(node) {
|
|
28
|
-
|
|
29
|
-
var _node$attrs = node.attrs,
|
|
30
|
-
shortName = _node$attrs.shortName,
|
|
31
|
-
id = _node$attrs.id,
|
|
32
|
-
text = _node$attrs.text;
|
|
33
|
-
var attrs = {
|
|
34
|
-
'data-emoji-short-name': shortName,
|
|
35
|
-
'data-emoji-id': id,
|
|
36
|
-
'data-emoji-text': text,
|
|
37
|
-
contenteditable: 'false',
|
|
38
|
-
style: (0, _lazyNodeView.convertToInlineCss)({
|
|
39
|
-
content: "''",
|
|
40
|
-
fill: "var(--ds-background-neutral, #091E420F)",
|
|
41
|
-
minWidth: "20px",
|
|
42
|
-
width: "20px",
|
|
43
|
-
height: "20px",
|
|
44
|
-
position: 'relative',
|
|
45
|
-
margin: '-1px 0',
|
|
46
|
-
display: 'inline-block',
|
|
47
|
-
background: "var(--ds-background-neutral, #091E420F)",
|
|
48
|
-
borderRadius: "var(--ds-border-radius-100, 3px)",
|
|
49
|
-
overflow: 'hidden',
|
|
50
|
-
verticalAlign: 'middle',
|
|
51
|
-
whiteSpace: 'nowrap',
|
|
52
|
-
textAlign: 'center'
|
|
53
|
-
}),
|
|
54
|
-
'aria-busy': 'true',
|
|
55
|
-
'aria-label': shortName,
|
|
56
|
-
class: 'emoji-common-placeholder'
|
|
57
|
-
};
|
|
58
|
-
return ['span', attrs];
|
|
29
|
+
return emojiToDom(node);
|
|
59
30
|
}
|
|
60
31
|
});
|
|
61
|
-
};
|
|
32
|
+
};
|
|
33
|
+
function emojiToDom(node) {
|
|
34
|
+
// From packages/elements/emoji/src/components/common/EmojiPlaceholder.tsx
|
|
35
|
+
var _node$attrs = node.attrs,
|
|
36
|
+
shortName = _node$attrs.shortName,
|
|
37
|
+
id = _node$attrs.id,
|
|
38
|
+
text = _node$attrs.text;
|
|
39
|
+
var attrs = {
|
|
40
|
+
'data-emoji-short-name': shortName,
|
|
41
|
+
'data-emoji-id': id,
|
|
42
|
+
'data-emoji-text': text,
|
|
43
|
+
contenteditable: 'false',
|
|
44
|
+
style: (0, _lazyNodeView.convertToInlineCss)({
|
|
45
|
+
content: "''",
|
|
46
|
+
fill: "var(--ds-background-neutral, #091E420F)",
|
|
47
|
+
minWidth: "20px",
|
|
48
|
+
width: "20px",
|
|
49
|
+
height: "20px",
|
|
50
|
+
position: 'relative',
|
|
51
|
+
margin: '-1px 0',
|
|
52
|
+
display: 'inline-block',
|
|
53
|
+
background: "var(--ds-background-neutral, #091E420F)",
|
|
54
|
+
borderRadius: "var(--ds-border-radius-100, 3px)",
|
|
55
|
+
overflow: 'hidden',
|
|
56
|
+
verticalAlign: 'middle',
|
|
57
|
+
whiteSpace: 'nowrap',
|
|
58
|
+
textAlign: 'center'
|
|
59
|
+
}),
|
|
60
|
+
'aria-busy': 'true',
|
|
61
|
+
'aria-label': shortName,
|
|
62
|
+
class: 'emoji-common-placeholder'
|
|
63
|
+
};
|
|
64
|
+
return ['span', attrs];
|
|
65
|
+
}
|
|
@@ -14,8 +14,9 @@ import CommentIcon from '@atlaskit/icon/core/comment';
|
|
|
14
14
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
15
15
|
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
16
16
|
import { createEmojiFragment, insertEmoji } from './editor-commands/insert-emoji';
|
|
17
|
-
import { EmojiNodeView } from './nodeviews/emoji';
|
|
17
|
+
import { EmojiNodeView as EmojiNodeViewReact } from './nodeviews/emoji';
|
|
18
18
|
import { emojiNodeSpec } from './nodeviews/emojiNodeSpec';
|
|
19
|
+
import { EmojiNodeView } from './nodeviews/EmojiNodeView';
|
|
19
20
|
import { ACTIONS, openTypeAhead as openTypeAheadAction, setAsciiMap as setAsciiMapAction, setInlineEmojiPopupOpen, setProvider as setProviderAction } from './pm-plugins/actions';
|
|
20
21
|
import { inputRulePlugin as asciiInputRulePlugin } from './pm-plugins/ascii-input-rules';
|
|
21
22
|
import { InlineEmojiPopup } from './ui/InlineEmojiPopup';
|
|
@@ -332,7 +333,7 @@ export const emojiPlugin = ({
|
|
|
332
333
|
var _api$editorViewMode, _api$editorViewMode$s;
|
|
333
334
|
return (api === null || api === void 0 ? void 0 : (_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 ? void 0 : (_api$editorViewMode$s = _api$editorViewMode.sharedState.currentState()) === null || _api$editorViewMode$s === void 0 ? void 0 : _api$editorViewMode$s.mode) === 'view';
|
|
334
335
|
};
|
|
335
|
-
if (!isViewMode()
|
|
336
|
+
if (!isViewMode()) {
|
|
336
337
|
return undefined;
|
|
337
338
|
}
|
|
338
339
|
const onClick = (stateFromClickEvent, dispatch) => {
|
|
@@ -496,15 +497,22 @@ export function createEmojiPlugin(pmPluginFactoryParams, options, api) {
|
|
|
496
497
|
},
|
|
497
498
|
props: {
|
|
498
499
|
nodeViews: {
|
|
499
|
-
emoji:
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
options,
|
|
500
|
+
emoji: (node, view, getPos, decorations) => {
|
|
501
|
+
return editorExperiment('platform_editor_vanilla_dom', true, {
|
|
502
|
+
exposure: true
|
|
503
|
+
}) ? new EmojiNodeView(node, {
|
|
504
|
+
intl: pmPluginFactoryParams.getIntl(),
|
|
505
505
|
api
|
|
506
|
-
}
|
|
507
|
-
|
|
506
|
+
}) : getInlineNodeViewProducer({
|
|
507
|
+
pmPluginFactoryParams,
|
|
508
|
+
Component: EmojiNodeViewReact,
|
|
509
|
+
extraComponentProps: {
|
|
510
|
+
providerFactory: pmPluginFactoryParams.providerFactory,
|
|
511
|
+
options,
|
|
512
|
+
api
|
|
513
|
+
}
|
|
514
|
+
})(node, view, getPos, decorations);
|
|
515
|
+
}
|
|
508
516
|
}
|
|
509
517
|
},
|
|
510
518
|
view(editorView) {
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import { messages, EmojiSharedCssClassName, defaultEmojiHeight } from '@atlaskit/editor-common/emoji';
|
|
3
|
+
import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
|
|
4
|
+
import { emojiToDom } from './emojiNodeSpec';
|
|
5
|
+
export class EmojiNodeView {
|
|
6
|
+
constructor(node, {
|
|
7
|
+
intl,
|
|
8
|
+
api
|
|
9
|
+
}) {
|
|
10
|
+
_defineProperty(this, "destroy", () => {});
|
|
11
|
+
// The pure `span` element will be used as a worse fallback only
|
|
12
|
+
// if DOMSerializer.renderSpec() in constructor fails.
|
|
13
|
+
_defineProperty(this, "dom", document.createElement('span'));
|
|
14
|
+
this.node = node;
|
|
15
|
+
this.intl = intl;
|
|
16
|
+
try {
|
|
17
|
+
var _api$emoji, _sharedState$currentS;
|
|
18
|
+
const {
|
|
19
|
+
dom
|
|
20
|
+
} = DOMSerializer.renderSpec(document, emojiToDom(this.node));
|
|
21
|
+
if (!(dom instanceof HTMLElement)) {
|
|
22
|
+
// It's safe to throw error here because, the code is wrapped in try-catch.
|
|
23
|
+
// However, it should never happen because `DOMSerializer.renderSpec()` should always return HTMLElement.
|
|
24
|
+
throw new Error('DOMSerializer.renderSpec() did not return HTMLElement');
|
|
25
|
+
}
|
|
26
|
+
this.dom = dom;
|
|
27
|
+
|
|
28
|
+
// We use the `emojiProvider` from the shared state
|
|
29
|
+
// because it supports the `emojiProvider` prop in the `ComposableEditor` options
|
|
30
|
+
// as well as the `emojiProvider` in the `EmojiPlugin` options.
|
|
31
|
+
const sharedState = api === null || api === void 0 ? void 0 : (_api$emoji = api.emoji) === null || _api$emoji === void 0 ? void 0 : _api$emoji.sharedState;
|
|
32
|
+
if (!sharedState) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
let emojiProvider = (_sharedState$currentS = sharedState.currentState()) === null || _sharedState$currentS === void 0 ? void 0 : _sharedState$currentS.emojiProvider;
|
|
36
|
+
if (emojiProvider) {
|
|
37
|
+
void this.updateDom(emojiProvider);
|
|
38
|
+
}
|
|
39
|
+
const unsubscribe = sharedState.onChange(({
|
|
40
|
+
nextSharedState
|
|
41
|
+
}) => {
|
|
42
|
+
if (emojiProvider === (nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.emojiProvider)) {
|
|
43
|
+
// Do not update if the provider is the same
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
emojiProvider = nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.emojiProvider;
|
|
47
|
+
void this.updateDom(emojiProvider);
|
|
48
|
+
});
|
|
49
|
+
this.destroy = () => {
|
|
50
|
+
unsubscribe();
|
|
51
|
+
};
|
|
52
|
+
} catch (error) {
|
|
53
|
+
// TODO: ED-27390 - send analytics event with error
|
|
54
|
+
|
|
55
|
+
this.renderFallback();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async updateDom(emojiProvider) {
|
|
59
|
+
// Clean up the DOM before rendering the new emoji
|
|
60
|
+
this.dom.innerHTML = '';
|
|
61
|
+
this.dom.style.cssText = '';
|
|
62
|
+
this.dom.classList.remove(EmojiSharedCssClassName.EMOJI_PLACEHOLDER);
|
|
63
|
+
this.dom.removeAttribute('aria-label'); // The label is set in the renderEmoji method
|
|
64
|
+
this.dom.removeAttribute('aria-busy');
|
|
65
|
+
|
|
66
|
+
// Each vanilla JS node implementation should have this data attribute
|
|
67
|
+
this.dom.setAttribute('data-prosemirror-node-view-type', 'vanilla');
|
|
68
|
+
try {
|
|
69
|
+
const {
|
|
70
|
+
shortName,
|
|
71
|
+
id,
|
|
72
|
+
text: fallback
|
|
73
|
+
} = this.node.attrs;
|
|
74
|
+
const emojiDescription = await (emojiProvider === null || emojiProvider === void 0 ? void 0 : emojiProvider.fetchByEmojiId({
|
|
75
|
+
id,
|
|
76
|
+
shortName,
|
|
77
|
+
fallback
|
|
78
|
+
}, true));
|
|
79
|
+
const emojiRepresentation = emojiDescription === null || emojiDescription === void 0 ? void 0 : emojiDescription.representation;
|
|
80
|
+
if (emojiDescription && this.isEmojiRepresentationSupported(emojiRepresentation)) {
|
|
81
|
+
this.renderEmoji(emojiDescription, emojiRepresentation);
|
|
82
|
+
} else {
|
|
83
|
+
// TODO: ED-27390 - send analytics event with info that emoji description is not supported
|
|
84
|
+
|
|
85
|
+
this.renderFallback();
|
|
86
|
+
}
|
|
87
|
+
} catch (error) {
|
|
88
|
+
// TODO: ED-27390 - send analytics event with error
|
|
89
|
+
|
|
90
|
+
this.renderFallback();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
isEmojiRepresentationSupported(representation) {
|
|
94
|
+
return !!representation && ('sprite' in representation || 'imagePath' in representation || 'mediaPath' in representation);
|
|
95
|
+
}
|
|
96
|
+
renderFallback() {
|
|
97
|
+
// TODO: ED-27390 - send analytics event with info that fallback was used
|
|
98
|
+
|
|
99
|
+
const fallbackElement = document.createElement('span');
|
|
100
|
+
fallbackElement.innerText = this.node.attrs.text || this.node.attrs.shortName;
|
|
101
|
+
fallbackElement.setAttribute('data-testid', `sprite-emoji-${this.node.attrs.shortName}`);
|
|
102
|
+
fallbackElement.setAttribute('data-emoji-type', 'sprite');
|
|
103
|
+
this.dom.appendChild(fallbackElement);
|
|
104
|
+
}
|
|
105
|
+
renderEmoji(description, representation) {
|
|
106
|
+
// TODO: ED-27390 - Add necessary analytics UFO events
|
|
107
|
+
|
|
108
|
+
const emojiType = 'sprite' in representation ? 'sprite' : 'image';
|
|
109
|
+
|
|
110
|
+
// Add wrapper for the emoji
|
|
111
|
+
const containerElement = document.createElement('span');
|
|
112
|
+
containerElement.setAttribute('role', 'img');
|
|
113
|
+
containerElement.classList.add(EmojiSharedCssClassName.EMOJI_CONTAINER);
|
|
114
|
+
containerElement.setAttribute('data-testid', `${emojiType}-emoji-${description.shortName}`);
|
|
115
|
+
containerElement.setAttribute('data-emoji-type', emojiType);
|
|
116
|
+
containerElement.setAttribute('aria-label', `${this.intl.formatMessage(messages.emojiNodeLabel)} ${description.shortName}`);
|
|
117
|
+
const emojiElement = 'sprite' in representation ? this.createSpriteEmojiElement(representation) : this.createImageEmojiElement(description, representation);
|
|
118
|
+
containerElement.appendChild(emojiElement);
|
|
119
|
+
this.dom.appendChild(containerElement);
|
|
120
|
+
}
|
|
121
|
+
createSpriteEmojiElement(representation) {
|
|
122
|
+
const spriteElement = document.createElement('span');
|
|
123
|
+
spriteElement.classList.add(EmojiSharedCssClassName.EMOJI_SPRITE);
|
|
124
|
+
const sprite = representation.sprite;
|
|
125
|
+
const xPositionInPercent = 100 / (sprite.column - 1) * representation.xIndex;
|
|
126
|
+
const yPositionInPercent = 100 / (sprite.row - 1) * representation.yIndex;
|
|
127
|
+
spriteElement.style.backgroundImage = `url(${sprite.url})`;
|
|
128
|
+
spriteElement.style.backgroundPosition = `${xPositionInPercent}% ${yPositionInPercent}%`;
|
|
129
|
+
spriteElement.style.backgroundSize = `${sprite.column * 100}% ${sprite.row * 100}%`;
|
|
130
|
+
spriteElement.style.width = `${defaultEmojiHeight}px`;
|
|
131
|
+
spriteElement.style.minWidth = `${defaultEmojiHeight}px`;
|
|
132
|
+
spriteElement.style.height = `${defaultEmojiHeight}px`;
|
|
133
|
+
spriteElement.style.minHeight = `${defaultEmojiHeight}px`;
|
|
134
|
+
return spriteElement;
|
|
135
|
+
}
|
|
136
|
+
createImageEmojiElement(emojiDescription, representation) {
|
|
137
|
+
const imageElement = document.createElement('img');
|
|
138
|
+
imageElement.classList.add(EmojiSharedCssClassName.EMOJI_IMAGE);
|
|
139
|
+
imageElement.src = 'imagePath' in representation ? representation.imagePath : representation.mediaPath;
|
|
140
|
+
imageElement.loading = 'lazy';
|
|
141
|
+
imageElement.alt = emojiDescription.name || emojiDescription.shortName;
|
|
142
|
+
if (representation.width && representation.height) {
|
|
143
|
+
imageElement.height = defaultEmojiHeight;
|
|
144
|
+
// Because img.width is round to the nearest integer.
|
|
145
|
+
imageElement.setAttribute('width', `${defaultEmojiHeight / representation.height * representation.width}`);
|
|
146
|
+
}
|
|
147
|
+
return imageElement;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -15,39 +15,40 @@ export const emojiNodeSpec = () => {
|
|
|
15
15
|
}
|
|
16
16
|
return {
|
|
17
17
|
...emoji,
|
|
18
|
-
toDOM: node =>
|
|
19
|
-
// From packages/elements/emoji/src/components/common/EmojiPlaceholder.tsx
|
|
20
|
-
const {
|
|
21
|
-
shortName,
|
|
22
|
-
id,
|
|
23
|
-
text
|
|
24
|
-
} = node.attrs;
|
|
25
|
-
const attrs = {
|
|
26
|
-
'data-emoji-short-name': shortName,
|
|
27
|
-
'data-emoji-id': id,
|
|
28
|
-
'data-emoji-text': text,
|
|
29
|
-
contenteditable: 'false',
|
|
30
|
-
style: convertToInlineCss({
|
|
31
|
-
content: "''",
|
|
32
|
-
fill: "var(--ds-background-neutral, #091E420F)",
|
|
33
|
-
minWidth: `20px`,
|
|
34
|
-
width: `20px`,
|
|
35
|
-
height: `20px`,
|
|
36
|
-
position: 'relative',
|
|
37
|
-
margin: '-1px 0',
|
|
38
|
-
display: 'inline-block',
|
|
39
|
-
background: "var(--ds-background-neutral, #091E420F)",
|
|
40
|
-
borderRadius: "var(--ds-border-radius-100, 3px)",
|
|
41
|
-
overflow: 'hidden',
|
|
42
|
-
verticalAlign: 'middle',
|
|
43
|
-
whiteSpace: 'nowrap',
|
|
44
|
-
textAlign: 'center'
|
|
45
|
-
}),
|
|
46
|
-
'aria-busy': 'true',
|
|
47
|
-
'aria-label': shortName,
|
|
48
|
-
class: 'emoji-common-placeholder'
|
|
49
|
-
};
|
|
50
|
-
return ['span', attrs];
|
|
51
|
-
}
|
|
18
|
+
toDOM: node => emojiToDom(node)
|
|
52
19
|
};
|
|
53
|
-
};
|
|
20
|
+
};
|
|
21
|
+
export function emojiToDom(node) {
|
|
22
|
+
// From packages/elements/emoji/src/components/common/EmojiPlaceholder.tsx
|
|
23
|
+
const {
|
|
24
|
+
shortName,
|
|
25
|
+
id,
|
|
26
|
+
text
|
|
27
|
+
} = node.attrs;
|
|
28
|
+
const attrs = {
|
|
29
|
+
'data-emoji-short-name': shortName,
|
|
30
|
+
'data-emoji-id': id,
|
|
31
|
+
'data-emoji-text': text,
|
|
32
|
+
contenteditable: 'false',
|
|
33
|
+
style: convertToInlineCss({
|
|
34
|
+
content: "''",
|
|
35
|
+
fill: "var(--ds-background-neutral, #091E420F)",
|
|
36
|
+
minWidth: `20px`,
|
|
37
|
+
width: `20px`,
|
|
38
|
+
height: `20px`,
|
|
39
|
+
position: 'relative',
|
|
40
|
+
margin: '-1px 0',
|
|
41
|
+
display: 'inline-block',
|
|
42
|
+
background: "var(--ds-background-neutral, #091E420F)",
|
|
43
|
+
borderRadius: "var(--ds-border-radius-100, 3px)",
|
|
44
|
+
overflow: 'hidden',
|
|
45
|
+
verticalAlign: 'middle',
|
|
46
|
+
whiteSpace: 'nowrap',
|
|
47
|
+
textAlign: 'center'
|
|
48
|
+
}),
|
|
49
|
+
'aria-busy': 'true',
|
|
50
|
+
'aria-label': shortName,
|
|
51
|
+
class: 'emoji-common-placeholder'
|
|
52
|
+
};
|
|
53
|
+
return ['span', attrs];
|
|
54
|
+
}
|
package/dist/esm/emojiPlugin.js
CHANGED
|
@@ -19,8 +19,9 @@ import CommentIcon from '@atlaskit/icon/core/comment';
|
|
|
19
19
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
20
20
|
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments';
|
|
21
21
|
import { createEmojiFragment, insertEmoji } from './editor-commands/insert-emoji';
|
|
22
|
-
import { EmojiNodeView } from './nodeviews/emoji';
|
|
22
|
+
import { EmojiNodeView as EmojiNodeViewReact } from './nodeviews/emoji';
|
|
23
23
|
import { emojiNodeSpec } from './nodeviews/emojiNodeSpec';
|
|
24
|
+
import { EmojiNodeView } from './nodeviews/EmojiNodeView';
|
|
24
25
|
import { ACTIONS, openTypeAhead as openTypeAheadAction, setAsciiMap as setAsciiMapAction, setInlineEmojiPopupOpen, setProvider as setProviderAction } from './pm-plugins/actions';
|
|
25
26
|
import { inputRulePlugin as asciiInputRulePlugin } from './pm-plugins/ascii-input-rules';
|
|
26
27
|
import { InlineEmojiPopup } from './ui/InlineEmojiPopup';
|
|
@@ -357,7 +358,7 @@ export var emojiPlugin = function emojiPlugin(_ref2) {
|
|
|
357
358
|
var _api$editorViewMode;
|
|
358
359
|
return (api === null || api === void 0 || (_api$editorViewMode = api.editorViewMode) === null || _api$editorViewMode === void 0 || (_api$editorViewMode = _api$editorViewMode.sharedState.currentState()) === null || _api$editorViewMode === void 0 ? void 0 : _api$editorViewMode.mode) === 'view';
|
|
359
360
|
};
|
|
360
|
-
if (!isViewMode()
|
|
361
|
+
if (!isViewMode()) {
|
|
361
362
|
return undefined;
|
|
362
363
|
}
|
|
363
364
|
var onClick = function onClick(stateFromClickEvent, dispatch) {
|
|
@@ -527,15 +528,22 @@ export function createEmojiPlugin(pmPluginFactoryParams, options, api) {
|
|
|
527
528
|
},
|
|
528
529
|
props: {
|
|
529
530
|
nodeViews: {
|
|
530
|
-
emoji:
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
options: options,
|
|
531
|
+
emoji: function emoji(node, view, getPos, decorations) {
|
|
532
|
+
return editorExperiment('platform_editor_vanilla_dom', true, {
|
|
533
|
+
exposure: true
|
|
534
|
+
}) ? new EmojiNodeView(node, {
|
|
535
|
+
intl: pmPluginFactoryParams.getIntl(),
|
|
536
536
|
api: api
|
|
537
|
-
}
|
|
538
|
-
|
|
537
|
+
}) : getInlineNodeViewProducer({
|
|
538
|
+
pmPluginFactoryParams: pmPluginFactoryParams,
|
|
539
|
+
Component: EmojiNodeViewReact,
|
|
540
|
+
extraComponentProps: {
|
|
541
|
+
providerFactory: pmPluginFactoryParams.providerFactory,
|
|
542
|
+
options: options,
|
|
543
|
+
api: api
|
|
544
|
+
}
|
|
545
|
+
})(node, view, getPos, decorations);
|
|
546
|
+
}
|
|
539
547
|
}
|
|
540
548
|
},
|
|
541
549
|
view: function view(editorView) {
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
|
|
2
|
+
import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
|
|
3
|
+
import _createClass from "@babel/runtime/helpers/createClass";
|
|
4
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
5
|
+
import _regeneratorRuntime from "@babel/runtime/regenerator";
|
|
6
|
+
import { messages, EmojiSharedCssClassName, defaultEmojiHeight } from '@atlaskit/editor-common/emoji';
|
|
7
|
+
import { DOMSerializer } from '@atlaskit/editor-prosemirror/model';
|
|
8
|
+
import { emojiToDom } from './emojiNodeSpec';
|
|
9
|
+
export var EmojiNodeView = /*#__PURE__*/function () {
|
|
10
|
+
function EmojiNodeView(node, _ref) {
|
|
11
|
+
var _this = this;
|
|
12
|
+
var intl = _ref.intl,
|
|
13
|
+
api = _ref.api;
|
|
14
|
+
_classCallCheck(this, EmojiNodeView);
|
|
15
|
+
_defineProperty(this, "destroy", function () {});
|
|
16
|
+
// The pure `span` element will be used as a worse fallback only
|
|
17
|
+
// if DOMSerializer.renderSpec() in constructor fails.
|
|
18
|
+
_defineProperty(this, "dom", document.createElement('span'));
|
|
19
|
+
this.node = node;
|
|
20
|
+
this.intl = intl;
|
|
21
|
+
try {
|
|
22
|
+
var _api$emoji, _sharedState$currentS;
|
|
23
|
+
var _DOMSerializer$render = DOMSerializer.renderSpec(document, emojiToDom(this.node)),
|
|
24
|
+
dom = _DOMSerializer$render.dom;
|
|
25
|
+
if (!(dom instanceof HTMLElement)) {
|
|
26
|
+
// It's safe to throw error here because, the code is wrapped in try-catch.
|
|
27
|
+
// However, it should never happen because `DOMSerializer.renderSpec()` should always return HTMLElement.
|
|
28
|
+
throw new Error('DOMSerializer.renderSpec() did not return HTMLElement');
|
|
29
|
+
}
|
|
30
|
+
this.dom = dom;
|
|
31
|
+
|
|
32
|
+
// We use the `emojiProvider` from the shared state
|
|
33
|
+
// because it supports the `emojiProvider` prop in the `ComposableEditor` options
|
|
34
|
+
// as well as the `emojiProvider` in the `EmojiPlugin` options.
|
|
35
|
+
var sharedState = api === null || api === void 0 || (_api$emoji = api.emoji) === null || _api$emoji === void 0 ? void 0 : _api$emoji.sharedState;
|
|
36
|
+
if (!sharedState) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
var emojiProvider = (_sharedState$currentS = sharedState.currentState()) === null || _sharedState$currentS === void 0 ? void 0 : _sharedState$currentS.emojiProvider;
|
|
40
|
+
if (emojiProvider) {
|
|
41
|
+
void this.updateDom(emojiProvider);
|
|
42
|
+
}
|
|
43
|
+
var unsubscribe = sharedState.onChange(function (_ref2) {
|
|
44
|
+
var nextSharedState = _ref2.nextSharedState;
|
|
45
|
+
if (emojiProvider === (nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.emojiProvider)) {
|
|
46
|
+
// Do not update if the provider is the same
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
emojiProvider = nextSharedState === null || nextSharedState === void 0 ? void 0 : nextSharedState.emojiProvider;
|
|
50
|
+
void _this.updateDom(emojiProvider);
|
|
51
|
+
});
|
|
52
|
+
this.destroy = function () {
|
|
53
|
+
unsubscribe();
|
|
54
|
+
};
|
|
55
|
+
} catch (error) {
|
|
56
|
+
// TODO: ED-27390 - send analytics event with error
|
|
57
|
+
|
|
58
|
+
this.renderFallback();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return _createClass(EmojiNodeView, [{
|
|
62
|
+
key: "updateDom",
|
|
63
|
+
value: function () {
|
|
64
|
+
var _updateDom = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(emojiProvider) {
|
|
65
|
+
var _this$node$attrs, shortName, id, fallback, emojiDescription, emojiRepresentation;
|
|
66
|
+
return _regeneratorRuntime.wrap(function _callee$(_context) {
|
|
67
|
+
while (1) switch (_context.prev = _context.next) {
|
|
68
|
+
case 0:
|
|
69
|
+
// Clean up the DOM before rendering the new emoji
|
|
70
|
+
this.dom.innerHTML = '';
|
|
71
|
+
this.dom.style.cssText = '';
|
|
72
|
+
this.dom.classList.remove(EmojiSharedCssClassName.EMOJI_PLACEHOLDER);
|
|
73
|
+
this.dom.removeAttribute('aria-label'); // The label is set in the renderEmoji method
|
|
74
|
+
this.dom.removeAttribute('aria-busy');
|
|
75
|
+
|
|
76
|
+
// Each vanilla JS node implementation should have this data attribute
|
|
77
|
+
this.dom.setAttribute('data-prosemirror-node-view-type', 'vanilla');
|
|
78
|
+
_context.prev = 6;
|
|
79
|
+
_this$node$attrs = this.node.attrs, shortName = _this$node$attrs.shortName, id = _this$node$attrs.id, fallback = _this$node$attrs.text;
|
|
80
|
+
_context.next = 10;
|
|
81
|
+
return emojiProvider === null || emojiProvider === void 0 ? void 0 : emojiProvider.fetchByEmojiId({
|
|
82
|
+
id: id,
|
|
83
|
+
shortName: shortName,
|
|
84
|
+
fallback: fallback
|
|
85
|
+
}, true);
|
|
86
|
+
case 10:
|
|
87
|
+
emojiDescription = _context.sent;
|
|
88
|
+
emojiRepresentation = emojiDescription === null || emojiDescription === void 0 ? void 0 : emojiDescription.representation;
|
|
89
|
+
if (emojiDescription && this.isEmojiRepresentationSupported(emojiRepresentation)) {
|
|
90
|
+
this.renderEmoji(emojiDescription, emojiRepresentation);
|
|
91
|
+
} else {
|
|
92
|
+
// TODO: ED-27390 - send analytics event with info that emoji description is not supported
|
|
93
|
+
|
|
94
|
+
this.renderFallback();
|
|
95
|
+
}
|
|
96
|
+
_context.next = 18;
|
|
97
|
+
break;
|
|
98
|
+
case 15:
|
|
99
|
+
_context.prev = 15;
|
|
100
|
+
_context.t0 = _context["catch"](6);
|
|
101
|
+
// TODO: ED-27390 - send analytics event with error
|
|
102
|
+
|
|
103
|
+
this.renderFallback();
|
|
104
|
+
case 18:
|
|
105
|
+
case "end":
|
|
106
|
+
return _context.stop();
|
|
107
|
+
}
|
|
108
|
+
}, _callee, this, [[6, 15]]);
|
|
109
|
+
}));
|
|
110
|
+
function updateDom(_x) {
|
|
111
|
+
return _updateDom.apply(this, arguments);
|
|
112
|
+
}
|
|
113
|
+
return updateDom;
|
|
114
|
+
}()
|
|
115
|
+
}, {
|
|
116
|
+
key: "isEmojiRepresentationSupported",
|
|
117
|
+
value: function isEmojiRepresentationSupported(representation) {
|
|
118
|
+
return !!representation && ('sprite' in representation || 'imagePath' in representation || 'mediaPath' in representation);
|
|
119
|
+
}
|
|
120
|
+
}, {
|
|
121
|
+
key: "renderFallback",
|
|
122
|
+
value: function renderFallback() {
|
|
123
|
+
// TODO: ED-27390 - send analytics event with info that fallback was used
|
|
124
|
+
|
|
125
|
+
var fallbackElement = document.createElement('span');
|
|
126
|
+
fallbackElement.innerText = this.node.attrs.text || this.node.attrs.shortName;
|
|
127
|
+
fallbackElement.setAttribute('data-testid', "sprite-emoji-".concat(this.node.attrs.shortName));
|
|
128
|
+
fallbackElement.setAttribute('data-emoji-type', 'sprite');
|
|
129
|
+
this.dom.appendChild(fallbackElement);
|
|
130
|
+
}
|
|
131
|
+
}, {
|
|
132
|
+
key: "renderEmoji",
|
|
133
|
+
value: function renderEmoji(description, representation) {
|
|
134
|
+
// TODO: ED-27390 - Add necessary analytics UFO events
|
|
135
|
+
|
|
136
|
+
var emojiType = 'sprite' in representation ? 'sprite' : 'image';
|
|
137
|
+
|
|
138
|
+
// Add wrapper for the emoji
|
|
139
|
+
var containerElement = document.createElement('span');
|
|
140
|
+
containerElement.setAttribute('role', 'img');
|
|
141
|
+
containerElement.classList.add(EmojiSharedCssClassName.EMOJI_CONTAINER);
|
|
142
|
+
containerElement.setAttribute('data-testid', "".concat(emojiType, "-emoji-").concat(description.shortName));
|
|
143
|
+
containerElement.setAttribute('data-emoji-type', emojiType);
|
|
144
|
+
containerElement.setAttribute('aria-label', "".concat(this.intl.formatMessage(messages.emojiNodeLabel), " ").concat(description.shortName));
|
|
145
|
+
var emojiElement = 'sprite' in representation ? this.createSpriteEmojiElement(representation) : this.createImageEmojiElement(description, representation);
|
|
146
|
+
containerElement.appendChild(emojiElement);
|
|
147
|
+
this.dom.appendChild(containerElement);
|
|
148
|
+
}
|
|
149
|
+
}, {
|
|
150
|
+
key: "createSpriteEmojiElement",
|
|
151
|
+
value: function createSpriteEmojiElement(representation) {
|
|
152
|
+
var spriteElement = document.createElement('span');
|
|
153
|
+
spriteElement.classList.add(EmojiSharedCssClassName.EMOJI_SPRITE);
|
|
154
|
+
var sprite = representation.sprite;
|
|
155
|
+
var xPositionInPercent = 100 / (sprite.column - 1) * representation.xIndex;
|
|
156
|
+
var yPositionInPercent = 100 / (sprite.row - 1) * representation.yIndex;
|
|
157
|
+
spriteElement.style.backgroundImage = "url(".concat(sprite.url, ")");
|
|
158
|
+
spriteElement.style.backgroundPosition = "".concat(xPositionInPercent, "% ").concat(yPositionInPercent, "%");
|
|
159
|
+
spriteElement.style.backgroundSize = "".concat(sprite.column * 100, "% ").concat(sprite.row * 100, "%");
|
|
160
|
+
spriteElement.style.width = "".concat(defaultEmojiHeight, "px");
|
|
161
|
+
spriteElement.style.minWidth = "".concat(defaultEmojiHeight, "px");
|
|
162
|
+
spriteElement.style.height = "".concat(defaultEmojiHeight, "px");
|
|
163
|
+
spriteElement.style.minHeight = "".concat(defaultEmojiHeight, "px");
|
|
164
|
+
return spriteElement;
|
|
165
|
+
}
|
|
166
|
+
}, {
|
|
167
|
+
key: "createImageEmojiElement",
|
|
168
|
+
value: function createImageEmojiElement(emojiDescription, representation) {
|
|
169
|
+
var imageElement = document.createElement('img');
|
|
170
|
+
imageElement.classList.add(EmojiSharedCssClassName.EMOJI_IMAGE);
|
|
171
|
+
imageElement.src = 'imagePath' in representation ? representation.imagePath : representation.mediaPath;
|
|
172
|
+
imageElement.loading = 'lazy';
|
|
173
|
+
imageElement.alt = emojiDescription.name || emojiDescription.shortName;
|
|
174
|
+
if (representation.width && representation.height) {
|
|
175
|
+
imageElement.height = defaultEmojiHeight;
|
|
176
|
+
// Because img.width is round to the nearest integer.
|
|
177
|
+
imageElement.setAttribute('width', "".concat(defaultEmojiHeight / representation.height * representation.width));
|
|
178
|
+
}
|
|
179
|
+
return imageElement;
|
|
180
|
+
}
|
|
181
|
+
}]);
|
|
182
|
+
}();
|
|
@@ -18,37 +18,40 @@ export var emojiNodeSpec = function emojiNodeSpec() {
|
|
|
18
18
|
}
|
|
19
19
|
return _objectSpread(_objectSpread({}, emoji), {}, {
|
|
20
20
|
toDOM: function toDOM(node) {
|
|
21
|
-
|
|
22
|
-
var _node$attrs = node.attrs,
|
|
23
|
-
shortName = _node$attrs.shortName,
|
|
24
|
-
id = _node$attrs.id,
|
|
25
|
-
text = _node$attrs.text;
|
|
26
|
-
var attrs = {
|
|
27
|
-
'data-emoji-short-name': shortName,
|
|
28
|
-
'data-emoji-id': id,
|
|
29
|
-
'data-emoji-text': text,
|
|
30
|
-
contenteditable: 'false',
|
|
31
|
-
style: convertToInlineCss({
|
|
32
|
-
content: "''",
|
|
33
|
-
fill: "var(--ds-background-neutral, #091E420F)",
|
|
34
|
-
minWidth: "20px",
|
|
35
|
-
width: "20px",
|
|
36
|
-
height: "20px",
|
|
37
|
-
position: 'relative',
|
|
38
|
-
margin: '-1px 0',
|
|
39
|
-
display: 'inline-block',
|
|
40
|
-
background: "var(--ds-background-neutral, #091E420F)",
|
|
41
|
-
borderRadius: "var(--ds-border-radius-100, 3px)",
|
|
42
|
-
overflow: 'hidden',
|
|
43
|
-
verticalAlign: 'middle',
|
|
44
|
-
whiteSpace: 'nowrap',
|
|
45
|
-
textAlign: 'center'
|
|
46
|
-
}),
|
|
47
|
-
'aria-busy': 'true',
|
|
48
|
-
'aria-label': shortName,
|
|
49
|
-
class: 'emoji-common-placeholder'
|
|
50
|
-
};
|
|
51
|
-
return ['span', attrs];
|
|
21
|
+
return emojiToDom(node);
|
|
52
22
|
}
|
|
53
23
|
});
|
|
54
|
-
};
|
|
24
|
+
};
|
|
25
|
+
export function emojiToDom(node) {
|
|
26
|
+
// From packages/elements/emoji/src/components/common/EmojiPlaceholder.tsx
|
|
27
|
+
var _node$attrs = node.attrs,
|
|
28
|
+
shortName = _node$attrs.shortName,
|
|
29
|
+
id = _node$attrs.id,
|
|
30
|
+
text = _node$attrs.text;
|
|
31
|
+
var attrs = {
|
|
32
|
+
'data-emoji-short-name': shortName,
|
|
33
|
+
'data-emoji-id': id,
|
|
34
|
+
'data-emoji-text': text,
|
|
35
|
+
contenteditable: 'false',
|
|
36
|
+
style: convertToInlineCss({
|
|
37
|
+
content: "''",
|
|
38
|
+
fill: "var(--ds-background-neutral, #091E420F)",
|
|
39
|
+
minWidth: "20px",
|
|
40
|
+
width: "20px",
|
|
41
|
+
height: "20px",
|
|
42
|
+
position: 'relative',
|
|
43
|
+
margin: '-1px 0',
|
|
44
|
+
display: 'inline-block',
|
|
45
|
+
background: "var(--ds-background-neutral, #091E420F)",
|
|
46
|
+
borderRadius: "var(--ds-border-radius-100, 3px)",
|
|
47
|
+
overflow: 'hidden',
|
|
48
|
+
verticalAlign: 'middle',
|
|
49
|
+
whiteSpace: 'nowrap',
|
|
50
|
+
textAlign: 'center'
|
|
51
|
+
}),
|
|
52
|
+
'aria-busy': 'true',
|
|
53
|
+
'aria-label': shortName,
|
|
54
|
+
class: 'emoji-common-placeholder'
|
|
55
|
+
};
|
|
56
|
+
return ['span', attrs];
|
|
57
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { IntlShape } from 'react-intl-next';
|
|
2
|
+
import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
|
|
3
|
+
import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
4
|
+
import type { NodeView } from '@atlaskit/editor-prosemirror/view';
|
|
5
|
+
import type { EmojiPlugin } from '../emojiPluginType';
|
|
6
|
+
interface Params {
|
|
7
|
+
intl: IntlShape;
|
|
8
|
+
api: ExtractInjectionAPI<EmojiPlugin> | undefined;
|
|
9
|
+
}
|
|
10
|
+
export declare class EmojiNodeView implements NodeView {
|
|
11
|
+
private readonly node;
|
|
12
|
+
private readonly intl;
|
|
13
|
+
readonly destroy: () => void;
|
|
14
|
+
readonly dom: HTMLElement;
|
|
15
|
+
constructor(node: PMNode, { intl, api }: Params);
|
|
16
|
+
private updateDom;
|
|
17
|
+
private isEmojiRepresentationSupported;
|
|
18
|
+
private renderFallback;
|
|
19
|
+
private renderEmoji;
|
|
20
|
+
private createSpriteEmojiElement;
|
|
21
|
+
private createImageEmojiElement;
|
|
22
|
+
}
|
|
23
|
+
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { DOMOutputSpec, Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
1
2
|
/**
|
|
2
3
|
* Wrapper for ADF emoji node spec to augment toDOM implementation
|
|
3
4
|
* with fallback UI for lazy node view rendering / window virtualization
|
|
@@ -5,3 +6,4 @@
|
|
|
5
6
|
* @returns
|
|
6
7
|
*/
|
|
7
8
|
export declare const emojiNodeSpec: () => import("prosemirror-model").NodeSpec;
|
|
9
|
+
export declare function emojiToDom(node: PMNode): DOMOutputSpec;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { IntlShape } from 'react-intl-next';
|
|
2
|
+
import type { ExtractInjectionAPI } from '@atlaskit/editor-common/types';
|
|
3
|
+
import type { Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
4
|
+
import type { NodeView } from '@atlaskit/editor-prosemirror/view';
|
|
5
|
+
import type { EmojiPlugin } from '../emojiPluginType';
|
|
6
|
+
interface Params {
|
|
7
|
+
intl: IntlShape;
|
|
8
|
+
api: ExtractInjectionAPI<EmojiPlugin> | undefined;
|
|
9
|
+
}
|
|
10
|
+
export declare class EmojiNodeView implements NodeView {
|
|
11
|
+
private readonly node;
|
|
12
|
+
private readonly intl;
|
|
13
|
+
readonly destroy: () => void;
|
|
14
|
+
readonly dom: HTMLElement;
|
|
15
|
+
constructor(node: PMNode, { intl, api }: Params);
|
|
16
|
+
private updateDom;
|
|
17
|
+
private isEmojiRepresentationSupported;
|
|
18
|
+
private renderFallback;
|
|
19
|
+
private renderEmoji;
|
|
20
|
+
private createSpriteEmojiElement;
|
|
21
|
+
private createImageEmojiElement;
|
|
22
|
+
}
|
|
23
|
+
export {};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { DOMOutputSpec, Node as PMNode } from '@atlaskit/editor-prosemirror/model';
|
|
1
2
|
/**
|
|
2
3
|
* Wrapper for ADF emoji node spec to augment toDOM implementation
|
|
3
4
|
* with fallback UI for lazy node view rendering / window virtualization
|
|
@@ -5,3 +6,4 @@
|
|
|
5
6
|
* @returns
|
|
6
7
|
*/
|
|
7
8
|
export declare const emojiNodeSpec: () => import("prosemirror-model").NodeSpec;
|
|
9
|
+
export declare function emojiToDom(node: PMNode): DOMOutputSpec;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/editor-plugin-emoji",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "Emoji plugin for @atlaskit/editor-core",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -23,22 +23,22 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@atlaskit/adf-schema": "^47.6.0",
|
|
26
|
-
"@atlaskit/editor-common": "^102.
|
|
26
|
+
"@atlaskit/editor-common": "^102.18.0",
|
|
27
27
|
"@atlaskit/editor-plugin-analytics": "^2.2.0",
|
|
28
|
-
"@atlaskit/editor-plugin-annotation": "^2.
|
|
28
|
+
"@atlaskit/editor-plugin-annotation": "^2.2.0",
|
|
29
29
|
"@atlaskit/editor-plugin-base": "^2.3.0",
|
|
30
30
|
"@atlaskit/editor-plugin-editor-viewmode": "^3.0.0",
|
|
31
|
-
"@atlaskit/editor-plugin-type-ahead": "^2.
|
|
31
|
+
"@atlaskit/editor-plugin-type-ahead": "^2.3.0",
|
|
32
32
|
"@atlaskit/editor-prosemirror": "7.0.0",
|
|
33
33
|
"@atlaskit/editor-shared-styles": "^3.4.0",
|
|
34
34
|
"@atlaskit/emoji": "^69.0.0",
|
|
35
|
-
"@atlaskit/icon": "^25.
|
|
35
|
+
"@atlaskit/icon": "^25.5.0",
|
|
36
36
|
"@atlaskit/node-data-provider": "^4.1.0",
|
|
37
37
|
"@atlaskit/platform-feature-flags": "^1.1.0",
|
|
38
38
|
"@atlaskit/prosemirror-input-rules": "^3.3.0",
|
|
39
39
|
"@atlaskit/theme": "^18.0.0",
|
|
40
|
-
"@atlaskit/tmp-editor-statsig": "^4.
|
|
41
|
-
"@atlaskit/tokens": "^4.
|
|
40
|
+
"@atlaskit/tmp-editor-statsig": "^4.6.0",
|
|
41
|
+
"@atlaskit/tokens": "^4.6.0",
|
|
42
42
|
"@babel/runtime": "^7.0.0",
|
|
43
43
|
"@emotion/react": "^11.7.1",
|
|
44
44
|
"react-intl-next": "npm:react-intl@^5.18.1",
|
|
@@ -110,9 +110,6 @@
|
|
|
110
110
|
"editor_inline_comments_paste_insert_nodes": {
|
|
111
111
|
"type": "boolean"
|
|
112
112
|
},
|
|
113
|
-
"platform_inline_node_as_valid_annotation_selection": {
|
|
114
|
-
"type": "boolean"
|
|
115
|
-
},
|
|
116
113
|
"platform_editor_preload_emoji_picker": {
|
|
117
114
|
"type": "boolean"
|
|
118
115
|
}
|