@atlaskit/emoji 71.0.0 → 71.1.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.
Files changed (60) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/afm-cc/tsconfig.json +3 -0
  3. package/afm-products/tsconfig.json +3 -0
  4. package/dist/cjs/components/common/Emoji.js +141 -5
  5. package/dist/cjs/components/common/EmojiActions.compiled.css +8 -0
  6. package/dist/cjs/components/common/EmojiActions.js +69 -3
  7. package/dist/cjs/components/common/Popup.js +20 -5
  8. package/dist/cjs/components/common/ProductivityColorSelector.compiled.css +59 -0
  9. package/dist/cjs/components/common/ProductivityColorSelector.js +112 -0
  10. package/dist/cjs/components/common/ResourcedEmojiComponent.js +4 -1
  11. package/dist/cjs/components/common/TonePreviewButton.js +4 -2
  12. package/dist/cjs/components/i18n.js +10 -0
  13. package/dist/cjs/components/picker/EmojiPickerComponent.js +51 -36
  14. package/dist/cjs/components/picker/EmojiPickerList.js +36 -14
  15. package/dist/cjs/components/picker/VirtualList.js +4 -5
  16. package/dist/cjs/util/analytics/analytics.js +1 -1
  17. package/dist/cjs/util/hidden-emojis.js +33 -0
  18. package/dist/cjs/util/productivity-colors.js +51 -0
  19. package/dist/es2019/components/common/Emoji.js +115 -5
  20. package/dist/es2019/components/common/EmojiActions.compiled.css +8 -0
  21. package/dist/es2019/components/common/EmojiActions.js +67 -3
  22. package/dist/es2019/components/common/Popup.js +19 -5
  23. package/dist/es2019/components/common/ProductivityColorSelector.compiled.css +59 -0
  24. package/dist/es2019/components/common/ProductivityColorSelector.js +98 -0
  25. package/dist/es2019/components/common/ResourcedEmojiComponent.js +3 -1
  26. package/dist/es2019/components/common/TonePreviewButton.js +3 -2
  27. package/dist/es2019/components/i18n.js +10 -0
  28. package/dist/es2019/components/picker/EmojiPickerComponent.js +26 -14
  29. package/dist/es2019/components/picker/EmojiPickerList.js +32 -14
  30. package/dist/es2019/components/picker/VirtualList.js +4 -5
  31. package/dist/es2019/util/analytics/analytics.js +1 -1
  32. package/dist/es2019/util/hidden-emojis.js +25 -0
  33. package/dist/es2019/util/productivity-colors.js +37 -0
  34. package/dist/esm/components/common/Emoji.js +142 -6
  35. package/dist/esm/components/common/EmojiActions.compiled.css +8 -0
  36. package/dist/esm/components/common/EmojiActions.js +70 -4
  37. package/dist/esm/components/common/Popup.js +20 -5
  38. package/dist/esm/components/common/ProductivityColorSelector.compiled.css +59 -0
  39. package/dist/esm/components/common/ProductivityColorSelector.js +103 -0
  40. package/dist/esm/components/common/ResourcedEmojiComponent.js +4 -1
  41. package/dist/esm/components/common/TonePreviewButton.js +4 -2
  42. package/dist/esm/components/i18n.js +10 -0
  43. package/dist/esm/components/picker/EmojiPickerComponent.js +51 -36
  44. package/dist/esm/components/picker/EmojiPickerList.js +36 -14
  45. package/dist/esm/components/picker/VirtualList.js +4 -5
  46. package/dist/esm/util/analytics/analytics.js +1 -1
  47. package/dist/esm/util/hidden-emojis.js +27 -0
  48. package/dist/esm/util/productivity-colors.js +45 -0
  49. package/dist/types/components/common/Emoji.d.ts +5 -0
  50. package/dist/types/components/common/EmojiActions.d.ts +6 -0
  51. package/dist/types/components/common/Popup.d.ts +1 -0
  52. package/dist/types/components/common/ProductivityColorSelector.d.ts +17 -0
  53. package/dist/types/components/common/ResourcedEmojiComponent.d.ts +6 -1
  54. package/dist/types/components/common/TonePreviewButton.d.ts +1 -0
  55. package/dist/types/components/i18n.d.ts +10 -0
  56. package/dist/types/components/picker/EmojiPickerList.d.ts +3 -0
  57. package/dist/types/types.d.ts +7 -0
  58. package/dist/types/util/hidden-emojis.d.ts +3 -0
  59. package/dist/types/util/productivity-colors.d.ts +8 -0
  60. package/package.json +3 -2
@@ -2,7 +2,7 @@
2
2
  import _extends from "@babel/runtime/helpers/extends";
3
3
  import "./Emoji.compiled.css";
4
4
  import { ax, ix } from "@compiled/react/runtime";
5
- import React, { useEffect, useCallback, useContext, useMemo, forwardRef } from 'react';
5
+ import React, { useEffect, useCallback, useContext, useMemo, useState, forwardRef } from 'react';
6
6
  import { IntlContext } from 'react-intl';
7
7
  import Tooltip from '@atlaskit/tooltip';
8
8
  import { shouldUseAltRepresentation } from '../../api/EmojiUtils';
@@ -21,6 +21,8 @@ import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
21
21
  import { getDocument } from '@atlaskit/browser-apis';
22
22
  import { fg } from '@atlaskit/platform-feature-flags';
23
23
  import { messages } from '../i18n';
24
+ import { isSSR } from '../../util/is-ssr';
25
+ import EmojiPlaceholder from './EmojiPlaceholder';
24
26
  const emojiSpriteContainer = null;
25
27
  const emojiImageContainer = null;
26
28
 
@@ -151,17 +153,123 @@ export const SpriteEmoji = props => {
151
153
  }))
152
154
  );
153
155
  };
154
- export const UnicodeEmoji = props => {
156
+ const unicodeEmojiCanvasSize = 128;
157
+ const unicodeEmojiCanvasFontSize = 124;
158
+ const unicodeEmojiCanvasTopPadding = 8;
159
+ const renderUnicodeEmojiToImagePath = async unicodeEmoji => {
160
+ const OffscreenCanvasCtor = globalThis['OffscreenCanvas'];
161
+ if (isSSR() || !OffscreenCanvasCtor || typeof URL === 'undefined') {
162
+ return undefined;
163
+ }
164
+ const canvas = new OffscreenCanvasCtor(unicodeEmojiCanvasSize, unicodeEmojiCanvasSize);
165
+ const context = canvas.getContext('2d');
166
+ if (!context) {
167
+ return undefined;
168
+ }
169
+ context.clearRect(0, 0, unicodeEmojiCanvasSize, unicodeEmojiCanvasSize);
170
+ context.textAlign = 'center';
171
+ context.textBaseline = 'middle';
172
+ context.font = `${unicodeEmojiCanvasFontSize}px sans-serif`;
173
+ context.fillText(unicodeEmoji, unicodeEmojiCanvasSize / 2, unicodeEmojiCanvasSize / 2 + unicodeEmojiCanvasTopPadding);
174
+ const blob = await canvas.convertToBlob({
175
+ type: 'image/png'
176
+ });
177
+ return URL.createObjectURL(blob);
178
+ };
179
+ const useUnicodeEmojiImage = unicodeEmoji => {
180
+ const [state, setState] = useState({
181
+ status: 'loading'
182
+ });
183
+ useEffect(() => {
184
+ let cancelled = false;
185
+ let imagePathToRevoke;
186
+ setState({
187
+ status: 'loading',
188
+ unicodeEmoji
189
+ });
190
+ void renderUnicodeEmojiToImagePath(unicodeEmoji).then(imagePath => {
191
+ if (cancelled) {
192
+ if (imagePath) {
193
+ URL.revokeObjectURL(imagePath);
194
+ }
195
+ return;
196
+ }
197
+ imagePathToRevoke = imagePath;
198
+ setState(imagePath ? {
199
+ status: 'ready',
200
+ unicodeEmoji,
201
+ imagePath
202
+ } : {
203
+ status: 'failed',
204
+ unicodeEmoji
205
+ });
206
+ }).catch(() => {
207
+ if (!cancelled) {
208
+ setState({
209
+ status: 'failed',
210
+ unicodeEmoji
211
+ });
212
+ }
213
+ });
214
+ return () => {
215
+ cancelled = true;
216
+ if (imagePathToRevoke) {
217
+ URL.revokeObjectURL(imagePathToRevoke);
218
+ }
219
+ };
220
+ }, [unicodeEmoji]);
221
+ return state;
222
+ };
223
+ const UnicodeEmojiImage = props => {
155
224
  const {
156
225
  emoji,
157
226
  fitToHeight,
227
+ showTooltip
228
+ } = props;
229
+ const emojiText = emoji.representation.unicodeEmoji;
230
+ const unicodeEmojiImage = useUnicodeEmojiImage(emojiText);
231
+ const hasCurrentEmojiImage = unicodeEmojiImage.unicodeEmoji === emojiText;
232
+ if (isSSR() || !hasCurrentEmojiImage || unicodeEmojiImage.status === 'loading') {
233
+ return /*#__PURE__*/React.createElement(EmojiPlaceholder, {
234
+ shortName: emoji.shortName,
235
+ showTooltip: showTooltip,
236
+ size: fitToHeight,
237
+ loading: true
238
+ });
239
+ }
240
+ if (unicodeEmojiImage.status === 'ready') {
241
+ return /*#__PURE__*/React.createElement(ImageEmoji, _extends({}, props, {
242
+ emoji: {
243
+ ...emoji,
244
+ representation: {
245
+ imagePath: unicodeEmojiImage.imagePath,
246
+ width: unicodeEmojiCanvasSize,
247
+ height: unicodeEmojiCanvasSize
248
+ }
249
+ }
250
+ }));
251
+ }
252
+ return /*#__PURE__*/React.createElement(EmojiPlaceholder, {
253
+ shortName: emoji.shortName,
254
+ showTooltip: showTooltip,
255
+ size: fitToHeight
256
+ });
257
+ };
258
+ export const UnicodeEmoji = props => {
259
+ var _props$fitToHeight;
260
+ const {
261
+ emoji,
158
262
  selected,
159
263
  selectOnHover,
160
- className
264
+ className,
265
+ renderUnicodeEmojiAsImage = true
161
266
  } = props;
267
+ if (renderUnicodeEmojiAsImage) {
268
+ return /*#__PURE__*/React.createElement(UnicodeEmojiImage, props);
269
+ }
162
270
  const classes = `${emojiNodeStyles} ${selected ? commonSelectedStyles : ''} ${selectOnHover ? selectOnHoverStyles : ''} ${className ? className : ''}`;
163
271
  const emojiText = emoji.representation.unicodeEmoji;
164
- const size = fitToHeight !== null && fitToHeight !== void 0 ? fitToHeight : defaultEmojiHeight;
272
+ const size = (_props$fitToHeight = props.fitToHeight) !== null && _props$fitToHeight !== void 0 ? _props$fitToHeight : defaultEmojiHeight;
165
273
  const style = {
166
274
  display: 'inline-block',
167
275
  // eslint-disable-next-line @atlaskit/design-system/use-tokens-typography
@@ -334,6 +442,7 @@ export const ImageEmoji = props => {
334
442
  );
335
443
  };
336
444
  export const EmojiNodeWrapper = /*#__PURE__*/forwardRef((props, ref) => {
445
+ var _emoji$name;
337
446
  const {
338
447
  emoji,
339
448
  fitToHeight,
@@ -355,11 +464,12 @@ export const EmojiNodeWrapper = /*#__PURE__*/forwardRef((props, ref) => {
355
464
  children,
356
465
  type,
357
466
  editorEmoji,
467
+ renderUnicodeEmojiAsImage,
358
468
  ...other
359
469
  } = props;
360
470
  const intl = useContext(IntlContext);
361
471
  const ariaLabel = intl && fg('platform_change_emoji_button_label') ? intl.formatMessage(messages.changeEmojiShortnameButtonLabel, {
362
- shortName: emoji.shortName
472
+ shortName: (_emoji$name = emoji.name) !== null && _emoji$name !== void 0 ? _emoji$name : emoji.shortName
363
473
  }) : editorEmoji ? undefined : emoji.shortName;
364
474
  return /*#__PURE__*/React.createElement("span", _extends({
365
475
  role: editorEmoji ? undefined : shouldBeInteractive ? 'button' : ariaLabel ? 'img' : 'presentation',
@@ -1,9 +1,13 @@
1
1
 
2
+ ._19itia51{border:var(--ds-border-width,1px) solid var(--ds-border,#0b120e24)}
3
+ ._2rkofajl{border-radius:var(--ds-radius-small,3px)}
2
4
  ._n7zl1uh4{border-bottom:var(--ds-border-width-selected,2px) solid var(--ds-border,#0b120e24)}._16jlidpf{flex-grow:0}
3
5
  ._16jlkb7n{flex-grow:1}
6
+ ._16qs130s{box-shadow:var(--ds-shadow-overlay,0 8px 9pt #1e1f2126,0 0 1px #1e1f214f)}
4
7
  ._16qsjgpa{box-shadow:0 1px 1px 0 var(--ds-border,#0b120e24)}
5
8
  ._18u019bv{margin-left:10px}
6
9
  ._18u0r5cr{margin-left:var(--ds-space-negative-050,-4px)}
10
+ ._19bv12x7{padding-left:var(--ds-space-075,6px)}
7
11
  ._19bvidpf{padding-left:0}
8
12
  ._1bahesu3{justify-content:flex-end}
9
13
  ._1e0c1txw{display:flex}
@@ -12,10 +16,14 @@
12
16
  ._1wpz1h6o{align-self:center}
13
17
  ._2hwx1i6y{margin-right:var(--ds-space-negative-025,-2px)}
14
18
  ._4cvr1h6o{align-items:center}
19
+ ._bfhk1bhr{background-color:var(--ds-surface-overlay,#fff)}
20
+ ._ca0q12x7{padding-top:var(--ds-space-075,6px)}
15
21
  ._ca0q1skh{padding-top:11px}
16
22
  ._i0dl1wug{flex-basis:auto}
17
23
  ._i0dlf1ug{flex-basis:0%}
24
+ ._n3td12x7{padding-bottom:var(--ds-space-075,6px)}
18
25
  ._n3td1crf{padding-bottom:9pt}
19
26
  ._otyr19bv{margin-bottom:10px}
20
27
  ._p12f3sup{max-width:285px}
28
+ ._u5f312x7{padding-right:var(--ds-space-075,6px)}
21
29
  ._u5f319bv{padding-right:10px}
@@ -3,13 +3,15 @@ import _extends from "@babel/runtime/helpers/extends";
3
3
  import "./EmojiActions.compiled.css";
4
4
  import * as React from 'react';
5
5
  import { ax, ix } from "@compiled/react/runtime";
6
- import { Fragment, useState, useRef, memo, useLayoutEffect, useCallback } from 'react';
6
+ import { Fragment, useState, useRef, memo, useLayoutEffect, useCallback, useEffect } from 'react';
7
7
  import { fg } from '@atlaskit/platform-feature-flags';
8
8
  import { FormattedMessage, injectIntl } from 'react-intl';
9
9
  import EmojiDeletePreview from './EmojiDeletePreview';
10
10
  import EmojiUploadPicker from './EmojiUploadPicker';
11
11
  import TonePreviewButton from './TonePreviewButton';
12
12
  import ToneSelector from './ToneSelector';
13
+ import ProductivityColorSelector, { productivityColorSelectorId } from './ProductivityColorSelector';
14
+ import Popup from './Popup';
13
15
  import { EmojiPickerListSearch } from '../picker/EmojiPickerListSearch';
14
16
  import { messages } from '../i18n';
15
17
  import AkButton from '@atlaskit/button/standard-button';
@@ -26,6 +28,7 @@ const addCustomEmoji = null;
26
28
  const addCustomEmojiButton = null;
27
29
  const emojiActionsWrapper = null;
28
30
  const emojiToneSelectorContainer = null;
31
+ const productivityColorPopup = null;
29
32
  const previewFooter = null;
30
33
  const previewFooterNew = null;
31
34
  export const emojiActionsTestId = 'emoji-actions';
@@ -68,10 +71,16 @@ export const AddOwnEmoji = props => {
68
71
  };
69
72
  const TonesWrapper = props => {
70
73
  const {
74
+ activeCategoryId,
75
+ onProductivityColorSelected,
76
+ productivityColorPreviewEmojis,
77
+ selectedProductivityColor,
71
78
  toneEmoji,
72
79
  selectedTone = DEFAULT_TONE,
73
80
  intl,
81
+ onToneClose,
74
82
  onToneOpen,
83
+ onToneToggle,
75
84
  showToneSelector
76
85
  } = props;
77
86
  const {
@@ -102,6 +111,41 @@ const TonesWrapper = props => {
102
111
  onToneSelected(toneValue);
103
112
  setFocusTonePreviewButton(true);
104
113
  }, [props]);
114
+ const onProductivityColorSelectedHandler = useCallback(color => {
115
+ onProductivityColorSelected === null || onProductivityColorSelected === void 0 ? void 0 : onProductivityColorSelected(color);
116
+ onToneClose();
117
+ setFocusTonePreviewButton(true);
118
+ }, [onProductivityColorSelected, onToneClose]);
119
+ const shouldShowProductivityColorSelector = !!(activeCategoryId === 'ATLASSIAN' && productivityColorPreviewEmojis && selectedProductivityColor && onProductivityColorSelected && FeatureGates.getExperimentValue('platform_teamoji_26_refresh_emoji_picker', 'isEnabled', false));
120
+ if (shouldShowProductivityColorSelector) {
121
+ const previewEmoji = (productivityColorPreviewEmojis === null || productivityColorPreviewEmojis === void 0 ? void 0 : productivityColorPreviewEmojis[selectedProductivityColor]) || Object.values(productivityColorPreviewEmojis || {})[0];
122
+ if (!previewEmoji) {
123
+ return null;
124
+ }
125
+ return /*#__PURE__*/React.createElement("div", {
126
+ className: ax(["_16jlkb7n _1o9zkb7n _i0dlf1ug _ca0q1skh _u5f319bv _n3td1crf _19bvidpf _1e0c1txw _1bahesu3"])
127
+ }, showToneSelector && tonePreviewButtonRef.current && /*#__PURE__*/React.createElement(Popup, {
128
+ target: tonePreviewButtonRef.current,
129
+ relativePosition: "below",
130
+ horizontalAlign: "end-to-start",
131
+ offsetY: 4,
132
+ zIndex: 510
133
+ }, /*#__PURE__*/React.createElement("div", {
134
+ className: ax(["_19itia51 _2rkofajl _bfhk1bhr _16qs130s _ca0q12x7 _u5f312x7 _n3td12x7 _19bv12x7"])
135
+ }, /*#__PURE__*/React.createElement(ProductivityColorSelector, {
136
+ colorPreviewEmojis: productivityColorPreviewEmojis,
137
+ selectedColor: selectedProductivityColor,
138
+ onColorSelected: onProductivityColorSelectedHandler
139
+ }))), /*#__PURE__*/React.createElement(TonePreviewButton, {
140
+ ref: tonePreviewButtonRef,
141
+ ariaControls: productivityColorSelectorId,
142
+ ariaExpanded: showToneSelector,
143
+ emoji: previewEmoji,
144
+ selectOnHover: true,
145
+ onSelected: onToneToggle,
146
+ ariaLabelText: formatMessage(messages.emojiSelectColorButtonAriaLabelText)
147
+ }));
148
+ }
105
149
  if (!toneEmoji) {
106
150
  return null;
107
151
  }
@@ -148,8 +192,13 @@ export const EmojiActions = props => {
148
192
  resultsCount = 0
149
193
  } = props;
150
194
  const [showToneSelector, setShowToneSelector] = useState(false);
195
+ const wasProductivityColorSelectorOpen = useRef(false);
196
+ const shouldUseProductivityColorControl = !!(props.activeCategoryId === 'ATLASSIAN' && props.productivityColorPreviewEmojis && props.selectedProductivityColor && props.onProductivityColorSelected && FeatureGates.getExperimentValue('platform_teamoji_26_refresh_emoji_picker', 'isEnabled', false));
151
197
  const onToneOpenHandler = useCallback(() => setShowToneSelector(true), []);
152
198
  const onToneCloseHandler = useCallback(() => setShowToneSelector(false), []);
199
+ const onToneToggleHandler = useCallback(() => {
200
+ setShowToneSelector(isOpen => !isOpen);
201
+ }, []);
153
202
  const onToneSelectedHandler = useCallback(toneValue => {
154
203
  setShowToneSelector(false);
155
204
  if (onToneSelected) {
@@ -157,11 +206,24 @@ export const EmojiActions = props => {
157
206
  }
158
207
  }, [onToneSelected]);
159
208
  const onMouseLeaveHandler = useCallback(() => {
209
+ if (shouldUseProductivityColorControl) {
210
+ return;
211
+ }
160
212
  if (showToneSelector && onToneSelectorCancelled) {
161
213
  onToneSelectorCancelled();
162
214
  }
163
215
  setShowToneSelector(false);
164
- }, [showToneSelector, onToneSelectorCancelled]);
216
+ }, [shouldUseProductivityColorControl, showToneSelector, onToneSelectorCancelled]);
217
+ useEffect(() => {
218
+ if (shouldUseProductivityColorControl && showToneSelector) {
219
+ wasProductivityColorSelectorOpen.current = true;
220
+ return;
221
+ }
222
+ if (!shouldUseProductivityColorControl && wasProductivityColorSelectorOpen.current) {
223
+ setShowToneSelector(false);
224
+ wasProductivityColorSelectorOpen.current = false;
225
+ }
226
+ }, [shouldUseProductivityColorControl, showToneSelector]);
165
227
  if (uploading) {
166
228
  return FeatureGates.getExperimentValue('platform_teamoji_26_refresh_emoji_picker', 'isEnabled', false) ? /*#__PURE__*/React.createElement("div", {
167
229
  className: ax(["_16jlidpf _1o9zidpf _i0dl1wug"])
@@ -207,11 +269,12 @@ export const EmojiActions = props => {
207
269
  onChange: onChange,
208
270
  query: query,
209
271
  resultsCount: resultsCount,
210
- isVisible: !showToneSelector
272
+ isVisible: !showToneSelector || shouldUseProductivityColorControl
211
273
  }), /*#__PURE__*/React.createElement(TonesWrapper, _extends({}, props, {
212
274
  onToneOpen: onToneOpenHandler,
213
275
  onToneClose: onToneCloseHandler,
214
276
  onToneSelected: onToneSelectedHandler,
277
+ onToneToggle: onToneToggleHandler,
215
278
  showToneSelector: showToneSelector
216
279
  })))) : /*#__PURE__*/React.createElement("div", {
217
280
  "data-testid": emojiActionsTestId,
@@ -229,6 +292,7 @@ export const EmojiActions = props => {
229
292
  onToneOpen: onToneOpenHandler,
230
293
  onToneClose: onToneCloseHandler,
231
294
  onToneSelected: onToneSelectedHandler,
295
+ onToneToggle: onToneToggleHandler,
232
296
  showToneSelector: showToneSelector
233
297
  }))), /*#__PURE__*/React.createElement(AddOwnEmoji, props));
234
298
  };
@@ -10,6 +10,7 @@ const getTargetNode = target => {
10
10
  const Popup = props => {
11
11
  const {
12
12
  relativePosition = 'auto',
13
+ horizontalAlign = 'start',
13
14
  offsetX = 0,
14
15
  offsetY = 0,
15
16
  zIndex = 9,
@@ -18,17 +19,27 @@ const Popup = props => {
18
19
  } = props;
19
20
  const popup = useRef();
20
21
  const [debounced, setDebounced] = useState(null);
22
+ const getLeftPosition = useCallback(box => {
23
+ if (horizontalAlign === 'end-to-start') {
24
+ return box.left - 152;
25
+ }
26
+ if (horizontalAlign === 'end') {
27
+ var _popup$current;
28
+ return box.right - (((_popup$current = popup.current) === null || _popup$current === void 0 ? void 0 : _popup$current.offsetWidth) || 0) + (offsetX || 0);
29
+ }
30
+ return box.left + (offsetX || 0);
31
+ }, [horizontalAlign, offsetX]);
21
32
  const applyBelowPosition = useCallback(() => {
22
33
  const targetNode = getTargetNode(target);
23
34
  if (targetNode && popup.current) {
24
35
  const box = targetNode.getBoundingClientRect();
25
36
  const top = box.bottom + (offsetY || 0);
26
- const left = box.left + (offsetX || 0);
37
+ const left = getLeftPosition(box);
27
38
  popup.current.style.top = `${top}px`;
28
39
  popup.current.style.bottom = '';
29
40
  popup.current.style.left = `${left}px`;
30
41
  }
31
- }, [offsetX, offsetY, target]);
42
+ }, [getLeftPosition, offsetY, target]);
32
43
  const applyAbovePosition = useCallback(() => {
33
44
  if (typeof window === 'undefined') {
34
45
  return;
@@ -37,12 +48,12 @@ const Popup = props => {
37
48
  if (targetNode && popup.current) {
38
49
  const box = targetNode.getBoundingClientRect();
39
50
  const bottom = window.innerHeight - box.top + (offsetY || 0);
40
- const left = box.left + (offsetX || 0);
51
+ const left = getLeftPosition(box);
41
52
  popup.current.style.top = '';
42
53
  popup.current.style.bottom = `${bottom}px`;
43
54
  popup.current.style.left = `${left}px`;
44
55
  }
45
- }, [offsetX, offsetY, target]);
56
+ }, [getLeftPosition, offsetY, target]);
46
57
  const applyAbsolutePosition = useCallback(() => {
47
58
  if (typeof window === 'undefined') {
48
59
  return;
@@ -97,6 +108,9 @@ const Popup = props => {
97
108
  }
98
109
  applyAbsolutePosition();
99
110
  renderPopup();
111
+ if (horizontalAlign !== 'start') {
112
+ applyAbsolutePosition();
113
+ }
100
114
  return () => {
101
115
  if (typeof window === 'undefined') {
102
116
  return;
@@ -107,7 +121,7 @@ const Popup = props => {
107
121
  document.body.removeChild(popup.current);
108
122
  }
109
123
  };
110
- }, [applyAbsolutePosition, handleResize, renderPopup]);
124
+ }, [applyAbsolutePosition, handleResize, horizontalAlign, renderPopup]);
111
125
  return /*#__PURE__*/React.createElement("div", null);
112
126
  };
113
127
  export default Popup;
@@ -0,0 +1,59 @@
1
+
2
+ ._1355fajl input+span{border-radius:var(--ds-radius-small,3px)}
3
+ ._19itidpf{border:0}
4
+ ._zulp1b66{gap:var(--ds-space-050,4px)}._1030zwfg input+span{height:2pc}
5
+ ._12hvze3t input{margin-right:var(--ds-space-0,0)}
6
+ ._12ultlke input{cursor:pointer}
7
+ ._13yj1o36 input:focus+span{outline-width:medium}
8
+ ._17muzwfg label{height:2pc}
9
+ ._18m915vq{overflow-y:hidden}
10
+ ._18u0ze3t{margin-left:var(--ds-space-0,0)}
11
+ ._19bvidpf{padding-left:0}
12
+ ._19pkze3t{margin-top:var(--ds-space-0,0)}
13
+ ._1bah1h6o{justify-content:center}
14
+ ._1bsbt94y{width:1px}
15
+ ._1bsbzwfg{width:2pc}
16
+ ._1e0c11p5{display:grid}
17
+ ._1e0c1txw{display:flex}
18
+ ._1e7sidpf input{opacity:0}
19
+ ._1eq11h6o label{align-items:center}
20
+ ._1nn0kb7n input{z-index:1}
21
+ ._1p1fzwfg label{width:2pc}
22
+ ._1pi91y54 input:focus+span{box-shadow:0 0 0 2px var(--ds-border-focused,#4688ec)}
23
+ ._1reo15vq{overflow-x:hidden}
24
+ ._1rgfze3t input{margin-left:var(--ds-space-0,0)}
25
+ ._1rgq13zc input:focus+span{transition-duration:0s,.2s}
26
+ ._1ul9idpf{min-width:0}
27
+ ._1vzltlke label{cursor:pointer}
28
+ ._1wqq1h6o label{justify-content:center}
29
+ ._2hwxze3t{margin-right:var(--ds-space-0,0)}
30
+ ._2x4gze3t input{margin-top:var(--ds-space-0,0)}
31
+ ._2z051y6t{grid-template-rows:repeat(2,2pc)}
32
+ ._4cvr1h6o{align-items:center}
33
+ ._4t3it94y{height:1px}
34
+ ._4t3izwfg{height:2pc}
35
+ ._5hv9zwfg input{width:2pc}
36
+ ._6rawzwfg input+span{width:2pc}
37
+ ._6vsr1h6o input+span{justify-content:center}
38
+ ._7kzvidpf input+span{padding-inline-end:0}
39
+ ._8qdiidpf input+span{padding-block-start:0}
40
+ ._ca0qidpf{padding-top:0}
41
+ ._ecyuidpf input+span{padding-block-end:0}
42
+ ._ev8ridpf input+span{padding-inline-start:0}
43
+ ._hxs6glyw input:focus+span{outline-style:none}
44
+ ._khufstnw input{position:absolute}
45
+ ._kqswh2mm{position:relative}
46
+ ._kqswstnw{position:absolute}
47
+ ._n3tdidpf{padding-bottom:0}
48
+ ._ngzz1txw input+span{display:flex}
49
+ ._o5721q9c{white-space:nowrap}
50
+ ._ogtohxbz{clip:rect(0 0 0 0)}
51
+ ._otyrze3t{margin-bottom:var(--ds-space-0,0)}
52
+ ._pytkzwfg input{height:2pc}
53
+ ._smb7plhp input:hover+span{background-color:var(--ds-background-neutral-hovered,#0b120e24)}
54
+ ._soq51h6o input+span{align-items:center}
55
+ ._szw2w5lj input:checked+span{box-shadow:0 0 0 2px var(--ds-border-selected,#1868db)}
56
+ ._tnsm1r31 input:focus+span{outline-color:currentColor}
57
+ ._u5f3idpf{padding-right:0}
58
+ ._x5bdze3t input{margin-bottom:var(--ds-space-0,0)}
59
+ ._yv0e1bbt{grid-template-columns:repeat(5,2pc)}
@@ -0,0 +1,98 @@
1
+ /* ProductivityColorSelector.tsx generated by @compiled/babel-plugin v0.39.1 */
2
+ import "./ProductivityColorSelector.compiled.css";
3
+ import * as React from 'react';
4
+ import { ax, ix } from "@compiled/react/runtime";
5
+ import { memo, useContext, useRef } from 'react';
6
+ import { IntlContext } from 'react-intl';
7
+ import { Radio } from '@atlaskit/radio';
8
+ import { productivityColors } from '../../util/productivity-colors';
9
+ import { messages } from '../i18n';
10
+ import Emoji from './Emoji';
11
+ export const productivityColorSelectorTestId = 'productivity-color-selector';
12
+ export const productivityColorSelectorId = 'emoji-picker-productivity-color-selector';
13
+ const selectorGrid = null;
14
+ const visuallyHiddenLegend = null;
15
+ const colorOption = null;
16
+ const colorOptionEmoji = null;
17
+ const getColorLabel = color => `${color.charAt(0).toUpperCase()}${color.slice(1)} productivity emoji colour`;
18
+ export const ProductivityColorSelector = ({
19
+ colorPreviewEmojis,
20
+ onColorSelected,
21
+ selectedColor
22
+ }) => {
23
+ const intl = useContext(IntlContext);
24
+ const radioRefs = useRef([]);
25
+ const hasFocusedSelectedColor = useRef(false);
26
+ const availableColors = productivityColors.filter(color => colorPreviewEmojis[color]);
27
+ const colorSelectorAriaLabel = intl ? intl.formatMessage(messages.emojiSelectColorListAriaLabelText) : messages.emojiSelectColorListAriaLabelText.defaultMessage;
28
+ if (!availableColors.length) {
29
+ return null;
30
+ }
31
+ const onArrowKey = (currentIndex, direction) => {
32
+ var _radioRefs$current$ne;
33
+ const nextIndex = (currentIndex + direction + availableColors.length) % availableColors.length;
34
+ (_radioRefs$current$ne = radioRefs.current[nextIndex]) === null || _radioRefs$current$ne === void 0 ? void 0 : _radioRefs$current$ne.focus();
35
+ };
36
+ const stopPickerDismissal = event => {
37
+ event.stopPropagation();
38
+ };
39
+ return /*#__PURE__*/React.createElement("fieldset", {
40
+ id: productivityColorSelectorId,
41
+ "data-testid": productivityColorSelectorTestId,
42
+ className: ax(["_19itidpf _zulp1b66 _ca0qidpf _u5f3idpf _n3tdidpf _19bvidpf _1e0c11p5 _yv0e1bbt _2z051y6t _19pkze3t _2hwxze3t _otyrze3t _18u0ze3t _1ul9idpf"])
43
+ }, /*#__PURE__*/React.createElement("legend", {
44
+ className: ax(["_19itidpf _1reo15vq _18m915vq _ca0qidpf _u5f3idpf _n3tdidpf _19bvidpf _ogtohxbz _4t3it94y _kqswstnw _o5721q9c _1bsbt94y"])
45
+ }, colorSelectorAriaLabel), availableColors.map((color, index) => {
46
+ const emoji = colorPreviewEmojis[color];
47
+ if (!emoji) {
48
+ return null;
49
+ }
50
+ const handleKeyDown = event => {
51
+ if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {
52
+ event.preventDefault();
53
+ onArrowKey(index, -1);
54
+ }
55
+ if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {
56
+ event.preventDefault();
57
+ onArrowKey(index, 1);
58
+ }
59
+ if (event.key === 'Enter') {
60
+ event.preventDefault();
61
+ onColorSelected(color);
62
+ }
63
+ };
64
+ return /*#__PURE__*/React.createElement("div", {
65
+ key: color,
66
+ className: ax(["_4t3izwfg _kqswh2mm _1bsbzwfg _1355fajl _soq51h6o _ngzz1txw _1030zwfg _6vsr1h6o _ecyuidpf _8qdiidpf _7kzvidpf _ev8ridpf _6rawzwfg _tnsm1r31 _hxs6glyw _13yj1o36 _1pi91y54 _1rgq13zc _1eq11h6o _1vzltlke _17muzwfg _1wqq1h6o _1p1fzwfg _12ultlke _pytkzwfg _2x4gze3t _12hvze3t _x5bdze3t _1rgfze3t _1e7sidpf _khufstnw _5hv9zwfg _1nn0kb7n _smb7plhp _szw2w5lj"])
67
+ }, /*#__PURE__*/React.createElement(Radio, {
68
+ ref: el => {
69
+ radioRefs.current[index] = el;
70
+ if (selectedColor === color) {
71
+ if (el && !hasFocusedSelectedColor.current) {
72
+ el.focus();
73
+ hasFocusedSelectedColor.current = true;
74
+ }
75
+ }
76
+ },
77
+ ariaLabel: getColorLabel(color),
78
+ name: "productivity-emoji-colour",
79
+ isChecked: selectedColor === color,
80
+ onChange: () => onColorSelected(color),
81
+ onClick: stopPickerDismissal,
82
+ onKeyDown: handleKeyDown,
83
+ onMouseDown: stopPickerDismissal,
84
+ testId: `productivity-color-${color}`,
85
+ value: color,
86
+ label: /*#__PURE__*/React.createElement("span", {
87
+ className: ax(["_4cvr1h6o _1e0c1txw _4t3izwfg _1bah1h6o _1bsbzwfg"])
88
+ }, /*#__PURE__*/React.createElement(Emoji, {
89
+ emoji: emoji,
90
+ shouldBeInteractive: false,
91
+ "aria-hidden": true,
92
+ fitToHeight: 24
93
+ }))
94
+ }));
95
+ }));
96
+ };
97
+ const _default_1 = /*#__PURE__*/memo(ProductivityColorSelector);
98
+ export default _default_1;
@@ -24,6 +24,7 @@ export const ResourcedEmojiComponent = ({
24
24
  optimistic = false,
25
25
  optimisticImageURL = undefined,
26
26
  editorEmoji,
27
+ renderUnicodeEmojiAsImage = true,
27
28
  pageTitleEmoji = false,
28
29
  placeholderXcss,
29
30
  onEmojiLoadSuccess,
@@ -259,7 +260,8 @@ export const ResourcedEmojiComponent = ({
259
260
  showTooltip: showTooltip,
260
261
  fitToHeight: fitToHeight,
261
262
  autoWidth: autoWidth,
262
- editorEmoji: editorEmoji
263
+ editorEmoji: editorEmoji,
264
+ renderUnicodeEmojiAsImage: renderUnicodeEmojiAsImage
263
265
  })));
264
266
  };
265
267
  export default ResourcedEmojiComponent;
@@ -14,6 +14,7 @@ export const TonePreviewButton = /*#__PURE__*/forwardRef((props, ref) => {
14
14
  const {
15
15
  emoji,
16
16
  selectOnHover,
17
+ ariaControls = 'emoji-picker-tone-selector',
17
18
  ariaLabelText,
18
19
  ariaExpanded,
19
20
  onSelected,
@@ -25,7 +26,7 @@ export const TonePreviewButton = /*#__PURE__*/forwardRef((props, ref) => {
25
26
  onClick: onSelected,
26
27
  "aria-label": ariaLabelText,
27
28
  "aria-expanded": ariaExpanded,
28
- "aria-controls": "emoji-picker-tone-selector"
29
+ "aria-controls": ariaControls
29
30
  // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
30
31
  ,
31
32
  style: {
@@ -45,7 +46,7 @@ export const TonePreviewButton = /*#__PURE__*/forwardRef((props, ref) => {
45
46
  onClick: onSelected,
46
47
  "aria-label": ariaLabelText,
47
48
  "aria-expanded": ariaExpanded,
48
- "aria-controls": "emoji-picker-tone-selector"
49
+ "aria-controls": ariaControls
49
50
  // eslint-disable-next-line @atlaskit/ui-styling-standard/enforce-style-prop -- Ignored via go/DSP-18766
50
51
  ,
51
52
  style: {
@@ -65,6 +65,16 @@ export const messages = defineMessages({
65
65
  defaultMessage: 'Choose your skin tone, {selectedTone} selected',
66
66
  description: 'Message indicating the purpose of the skin tone selection button and the selected tone'
67
67
  },
68
+ emojiSelectColorButtonAriaLabelText: {
69
+ id: 'fabric.emoji.select.color.ariaLabel',
70
+ defaultMessage: 'Productivity emoji color selector',
71
+ description: 'Message indicating the purpose of the color selection button and the selected color'
72
+ },
73
+ emojiSelectColorListAriaLabelText: {
74
+ id: 'fabric.emoji.select.color.list.ariaLabel',
75
+ defaultMessage: 'Productivity emoji colour selector',
76
+ description: 'Message indicating the purpose of the productivity emoji color list selector'
77
+ },
68
78
  emojiSelectSkinToneListAriaLabelText: {
69
79
  id: 'fabric.emoji.select.skin.list.ariaLabel',
70
80
  defaultMessage: 'Skin tone selector',