@atlaskit/emoji 70.2.8 → 70.3.1

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 CHANGED
@@ -1,5 +1,28 @@
1
1
  # @atlaskit/emoji
2
2
 
3
+ ## 70.3.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`79ba3fc378726`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/79ba3fc378726) -
8
+ A11y fixes
9
+ - Updated dependencies
10
+
11
+ ## 70.3.0
12
+
13
+ ### Minor Changes
14
+
15
+ - [`77a59d5fe3db0`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/77a59d5fe3db0) -
16
+ Add `getCachedEmojiType` optional method to `EmojiProvider` interface and implement it in
17
+ `EmojiResource` as a synchronous cache read (no network requests). Add `emojiIdToEmoji` shared
18
+ utility for converting hyphen-separated hex emoji IDs to Unicode strings (handles ZWJ sequences,
19
+ skin tone modifiers, flag sequences, keycap sequences, and variation selectors). Register
20
+ `platform_twemoji_removal_unicode_emojis` feature flag.
21
+
22
+ ### Patch Changes
23
+
24
+ - Updated dependencies
25
+
3
26
  ## 70.2.8
4
27
 
5
28
  ### Patch Changes
@@ -578,6 +578,34 @@ var EmojiResource = exports.EmojiResource = /*#__PURE__*/function (_ref) {
578
578
  return _this6.findById(id);
579
579
  }, undefined);
580
580
  }
581
+
582
+ /**
583
+ * Synchronously reads the emoji type from the local cache for the given emojiId.
584
+ * Returns `undefined` when the emoji is not in the cache or no repository is initialised.
585
+ * This is a pure cache read — no network requests are made.
586
+ */
587
+ }, {
588
+ key: "getCachedEmojiType",
589
+ value: function getCachedEmojiType(emojiId) {
590
+ if (!this.isRepositoryAvailable(this.emojiRepository)) {
591
+ return undefined;
592
+ }
593
+ var id = emojiId.id,
594
+ shortName = emojiId.shortName;
595
+ if (id) {
596
+ var emoji = this.emojiRepository.findById(id);
597
+ if (emoji) {
598
+ return emoji.type;
599
+ }
600
+ }
601
+ if (shortName) {
602
+ var _result = this.emojiRepository.findByShortName(shortName);
603
+ if (_result && !('then' in _result)) {
604
+ return _result.type;
605
+ }
606
+ }
607
+ return undefined;
608
+ }
581
609
  }, {
582
610
  key: "findInCategory",
583
611
  value: function findInCategory(categoryId) {
@@ -19,22 +19,22 @@ var messages = exports.messages = (0, _reactIntl.defineMessages)({
19
19
  deleteEmojiTitle: {
20
20
  id: 'fabric.emoji.delete.title',
21
21
  defaultMessage: 'Remove emoji',
22
- description: 'Title for emoji removal dialog'
22
+ description: 'Heading text shown at the top of the emoji removal confirmation dialog, prompting the user to confirm removing the selected custom emoji.'
23
23
  },
24
24
  deleteEmojiDescription: {
25
25
  id: 'fabric.emoji.delete.description',
26
26
  defaultMessage: 'All existing instances of this emoji will be replaced with {emojiShortName}',
27
- description: 'Description for emoji removal dialog'
27
+ description: 'Body text shown inside the emoji removal confirmation dialog. The placeholder {emojiShortName} will be substituted with the short name (e.g. ":smile:") of the replacement emoji.'
28
28
  },
29
29
  deleteEmojiLabel: {
30
30
  id: 'fabric.emoji.delete.label',
31
31
  defaultMessage: 'Remove',
32
- description: 'Button label to remove emoji'
32
+ description: 'Label for the primary action button in the emoji removal confirmation dialog that confirms and executes the removal.'
33
33
  },
34
34
  addCustomEmojiLabel: {
35
35
  id: 'fabric.emoji.add.custom.emoji.label',
36
36
  defaultMessage: 'Add your own emoji',
37
- description: 'Button label to add custom emoji'
37
+ description: 'Label for the button in the emoji picker that opens the custom emoji upload panel, allowing users to add their own emoji.'
38
38
  },
39
39
  emojiPlaceholder: {
40
40
  id: 'fabric.emoji.placeholder',
@@ -49,7 +49,7 @@ var messages = exports.messages = (0, _reactIntl.defineMessages)({
49
49
  emojiChooseFileTitle: {
50
50
  id: 'fabric.emoji.choose.file.title',
51
51
  defaultMessage: 'Choose file',
52
- description: 'Choose custom emoji file'
52
+ description: 'Label for the file chooser button in the custom emoji upload panel where users select an image file for their emoji.'
53
53
  },
54
54
  emojiChooseFileScreenReaderDescription: {
55
55
  id: 'fabric.emoji.choose.file.screenReaderDescription',
@@ -74,37 +74,37 @@ var messages = exports.messages = (0, _reactIntl.defineMessages)({
74
74
  emojiPreviewTitle: {
75
75
  id: 'fabric.emoji.preview.title',
76
76
  defaultMessage: 'Preview',
77
- description: 'Emoji preview title'
77
+ description: 'Section heading shown above the emoji preview area in the custom emoji upload panel.'
78
78
  },
79
79
  emojiPreview: {
80
80
  id: 'fabric.emoji.preview',
81
81
  defaultMessage: 'Your new emoji {emoji} looks great',
82
- description: 'Emoji preview'
82
+ description: 'Success text shown in the emoji preview area after the user uploads a new emoji image. The placeholder {emoji} will be substituted with the rendered emoji image.'
83
83
  },
84
84
  addEmojiLabel: {
85
85
  id: 'fabric.emoji.add.label',
86
86
  defaultMessage: 'Add emoji',
87
- description: 'verb - Button label to add emoji'
87
+ description: 'Label for the submit button in the custom emoji upload panel that saves the new emoji to the workspace.'
88
88
  },
89
89
  retryLabel: {
90
90
  id: 'fabric.emoji.retry.label',
91
91
  defaultMessage: 'Retry',
92
- description: 'verb - Button label to retry upload'
92
+ description: 'Label for the button in the custom emoji upload panel that retries a previously failed upload attempt.'
93
93
  },
94
94
  cancelLabel: {
95
95
  id: 'fabric.emoji.cancel.label',
96
96
  defaultMessage: 'Cancel',
97
- description: 'verb - button label to cancel operation'
97
+ description: 'Label for the cancel button in the custom emoji upload panel that dismisses the panel without saving.'
98
98
  },
99
99
  searchPlaceholder: {
100
100
  id: 'fabric.emoji.search.placeholder',
101
101
  defaultMessage: 'Search',
102
- description: 'Placeholder for search emoji field'
102
+ description: 'Placeholder text shown inside the emoji search input field in the emoji picker before the user types a query.'
103
103
  },
104
104
  searchLabel: {
105
105
  id: 'fabric.emoji.search.label',
106
106
  defaultMessage: 'Emoji name',
107
- description: 'verb - button label to search'
107
+ description: 'Accessible label for the emoji name search input field in the emoji picker, used by screen readers.'
108
108
  },
109
109
  searchResultsStatus: {
110
110
  id: 'fabric.emoji.search.status.count',
@@ -124,67 +124,67 @@ var messages = exports.messages = (0, _reactIntl.defineMessages)({
124
124
  categoriesSearchResults: {
125
125
  id: 'fabric.emoji.categories.search.results',
126
126
  defaultMessage: 'Search results',
127
- description: 'Emoji categories search results'
127
+ description: 'Category heading label shown in the emoji picker category bar when displaying results from a search query.'
128
128
  },
129
129
  frequentCategory: {
130
130
  id: 'fabric.emoji.category.frequent',
131
131
  defaultMessage: 'Frequent',
132
- description: 'Emoji frequent category'
132
+ description: 'Label for the Frequent category tab in the emoji picker, showing recently used emojis.'
133
133
  },
134
134
  peopleCategory: {
135
135
  id: 'fabric.emoji.category.people',
136
136
  defaultMessage: 'People',
137
- description: 'Emoji frequent category'
137
+ description: 'Label for the People category tab in the emoji picker, showing face and person emojis.'
138
138
  },
139
139
  natureCategory: {
140
140
  id: 'fabric.emoji.category.nature',
141
141
  defaultMessage: 'Nature',
142
- description: 'Emoji nature category'
142
+ description: 'Label for the Nature category tab in the emoji picker, showing animal and nature emojis.'
143
143
  },
144
144
  foodsCategory: {
145
145
  id: 'fabric.emoji.category.foods',
146
146
  defaultMessage: 'Food & Drink',
147
- description: 'Emoji Foods category'
147
+ description: 'Label for the Food and Drink category tab in the emoji picker.'
148
148
  },
149
149
  activityCategory: {
150
150
  id: 'fabric.emoji.category.activity',
151
151
  defaultMessage: 'Activity',
152
- description: 'Emoji activity category'
152
+ description: 'Label for the Activity category tab in the emoji picker, showing sports and activity emojis.'
153
153
  },
154
154
  placesCategory: {
155
155
  id: 'fabric.emoji.category.places',
156
156
  defaultMessage: 'Travel & Places',
157
- description: 'Emoji Places category'
157
+ description: 'Label for the Travel and Places category tab in the emoji picker, showing location and travel emojis.'
158
158
  },
159
159
  objectsCategory: {
160
160
  id: 'fabric.emoji.category.objects',
161
161
  defaultMessage: 'Objects',
162
- description: 'Emoji objects category'
162
+ description: 'Label for the Objects category tab in the emoji picker, showing everyday object emojis.'
163
163
  },
164
164
  symbolsCategory: {
165
165
  id: 'fabric.emoji.category.symbols',
166
166
  defaultMessage: 'Symbols',
167
- description: 'Emoji symbols category'
167
+ description: 'Label for the Symbols category tab in the emoji picker, showing symbol and sign emojis.'
168
168
  },
169
169
  flagsCategory: {
170
170
  id: 'fabric.emoji.category.flags',
171
171
  defaultMessage: 'Flags',
172
- description: 'Emoji flags category'
172
+ description: 'Label for the Flags category tab in the emoji picker, showing country and regional flag emojis.'
173
173
  },
174
174
  productivityCategory: {
175
175
  id: 'fabric.emoji.category.productivity',
176
176
  defaultMessage: 'Atlassian & productivity',
177
- description: 'Emoji Atlassian & productivity category'
177
+ description: 'Label for the Atlassian and productivity category tab in the emoji picker, showing Atlassian product and productivity emojis.'
178
178
  },
179
179
  userUploadsCustomCategory: {
180
180
  id: 'fabric.emoji.category.user.uploads',
181
181
  defaultMessage: 'Your uploads',
182
- description: 'User uploads in the custom category'
182
+ description: 'Label for the Your Uploads category tab in the emoji picker, showing custom emojis uploaded by the current user.'
183
183
  },
184
184
  allUploadsCustomCategory: {
185
185
  id: 'fabric.emoji.category.all.uploads',
186
186
  defaultMessage: 'All uploads',
187
- description: 'All uploads in the custom category'
187
+ description: 'Label for the All Uploads category tab in the emoji picker, showing all custom emojis uploaded across the workspace.'
188
188
  },
189
189
  deleteEmojiFailed: {
190
190
  id: 'fabric.emoji.error.delete.failed',
@@ -199,12 +199,12 @@ var messages = exports.messages = (0, _reactIntl.defineMessages)({
199
199
  emojiUploadFailed: {
200
200
  id: 'fabric.emoji.error.upload.failed',
201
201
  defaultMessage: 'Upload failed',
202
- description: 'Failed to upload emoji image'
202
+ description: 'Error message shown in the custom emoji upload panel when the image upload fails due to an error.'
203
203
  },
204
204
  emojiUploadTimeout: {
205
205
  id: 'fabric.emoji.error.upload.timeout',
206
206
  defaultMessage: 'Upload timed out',
207
- description: 'Failed to upload emoji image'
207
+ description: 'Error message shown in the custom emoji upload panel when the image upload fails because it timed out.'
208
208
  },
209
209
  emojiImageTooBig: {
210
210
  id: 'fabric.emoji.error.image.too.big',
@@ -214,7 +214,7 @@ var messages = exports.messages = (0, _reactIntl.defineMessages)({
214
214
  emojiPickerTitle: {
215
215
  id: 'fabric.emoji.picker',
216
216
  defaultMessage: 'Emoji picker',
217
- description: 'Aria label for emoji picker'
217
+ description: 'Accessible aria-label for the emoji picker dialog, used by screen readers to identify the picker.'
218
218
  },
219
219
  emojiPickerListPanel: {
220
220
  id: 'fabric.emoji.pickerlist.tabpanel',
@@ -20,7 +20,7 @@ var createEvent = function createEvent(eventType, action, actionSubject, actionS
20
20
  actionSubjectId: actionSubjectId,
21
21
  attributes: _objectSpread({
22
22
  packageName: "@atlaskit/emoji",
23
- packageVersion: "70.2.7"
23
+ packageVersion: "70.3.0"
24
24
  }, attributes)
25
25
  };
26
26
  };
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.emojiIdToEmoji = emojiIdToEmoji;
7
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
8
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
9
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
10
+ /**
11
+ * Converts an emoji ID (a hyphen-separated string of hex Unicode code points,
12
+ * e.g. `"1f44d"`, `"1f44d-1f3fb"`, `"1f468-200d-1f469-200d-1f467"`) into its
13
+ * corresponding Unicode string.
14
+ *
15
+ * Special handling:
16
+ * - Keycap sequences: U+20E3 (COMBINING ENCLOSING KEYCAP) must be preceded by
17
+ * U+FE0F (VARIATION SELECTOR-16) to form a well-defined keycap emoji.
18
+ * - Text-default emoji (characters whose default presentation is text) must be
19
+ * followed by U+FE0F to force emoji presentation.
20
+ *
21
+ * @returns The Unicode string for the emoji, or `undefined` if the input is
22
+ * invalid (empty string, non-hex segments, or out-of-range code points).
23
+ */
24
+ function emojiIdToEmoji(id) {
25
+ if (!id) {
26
+ return undefined;
27
+ }
28
+ var segments = id.split('-');
29
+ var codePoints = [];
30
+ var _iterator = _createForOfIteratorHelper(segments),
31
+ _step;
32
+ try {
33
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
34
+ var segment = _step.value;
35
+ if (!/^[0-9a-fA-F]+$/.test(segment)) {
36
+ return undefined;
37
+ }
38
+ var _cp = parseInt(segment, 16);
39
+ // Valid Unicode scalar values: 0x0000–0x10FFFF, excluding surrogates 0xD800–0xDFFF
40
+ if (isNaN(_cp) || _cp < 0 || _cp > 0x10ffff || _cp >= 0xd800 && _cp <= 0xdfff) {
41
+ return undefined;
42
+ }
43
+ codePoints.push(_cp);
44
+ }
45
+ } catch (err) {
46
+ _iterator.e(err);
47
+ } finally {
48
+ _iterator.f();
49
+ }
50
+ if (codePoints.length === 0) {
51
+ return undefined;
52
+ }
53
+
54
+ // Keycap sequences: digit/# /* followed by U+20E3 need U+FE0F between them.
55
+ // The keycap base characters are: 0x23 (#), 0x2A (*), 0x30–0x39 (0–9).
56
+ var KEYCAP_COMBINING = 0x20e3;
57
+ var VARIATION_SELECTOR_16 = 0xfe0f;
58
+ var processedCodePoints = [];
59
+ for (var i = 0; i < codePoints.length; i++) {
60
+ var cp = codePoints[i];
61
+ var next = codePoints[i + 1];
62
+ processedCodePoints.push(cp);
63
+
64
+ // Insert U+FE0F before U+20E3 if not already present
65
+ if (next === KEYCAP_COMBINING && cp !== VARIATION_SELECTOR_16) {
66
+ processedCodePoints.push(VARIATION_SELECTOR_16);
67
+ }
68
+ }
69
+ try {
70
+ return String.fromCodePoint.apply(String, processedCodePoints);
71
+ } catch (_unused) {
72
+ return undefined;
73
+ }
74
+ }
@@ -373,6 +373,34 @@ export class EmojiResource extends AbstractResource {
373
373
  }
374
374
  return this.retryIfLoading(() => this.findById(id), undefined);
375
375
  }
376
+
377
+ /**
378
+ * Synchronously reads the emoji type from the local cache for the given emojiId.
379
+ * Returns `undefined` when the emoji is not in the cache or no repository is initialised.
380
+ * This is a pure cache read — no network requests are made.
381
+ */
382
+ getCachedEmojiType(emojiId) {
383
+ if (!this.isRepositoryAvailable(this.emojiRepository)) {
384
+ return undefined;
385
+ }
386
+ const {
387
+ id,
388
+ shortName
389
+ } = emojiId;
390
+ if (id) {
391
+ const emoji = this.emojiRepository.findById(id);
392
+ if (emoji) {
393
+ return emoji.type;
394
+ }
395
+ }
396
+ if (shortName) {
397
+ const result = this.emojiRepository.findByShortName(shortName);
398
+ if (result && !('then' in result)) {
399
+ return result.type;
400
+ }
401
+ }
402
+ return undefined;
403
+ }
376
404
  findInCategory(categoryId) {
377
405
  if (this.isLoaded() && this.isRepositoryAvailable(this.emojiRepository)) {
378
406
  return Promise.resolve(this.emojiRepository.findInCategory(categoryId));
@@ -13,22 +13,22 @@ export const messages = defineMessages({
13
13
  deleteEmojiTitle: {
14
14
  id: 'fabric.emoji.delete.title',
15
15
  defaultMessage: 'Remove emoji',
16
- description: 'Title for emoji removal dialog'
16
+ description: 'Heading text shown at the top of the emoji removal confirmation dialog, prompting the user to confirm removing the selected custom emoji.'
17
17
  },
18
18
  deleteEmojiDescription: {
19
19
  id: 'fabric.emoji.delete.description',
20
20
  defaultMessage: 'All existing instances of this emoji will be replaced with {emojiShortName}',
21
- description: 'Description for emoji removal dialog'
21
+ description: 'Body text shown inside the emoji removal confirmation dialog. The placeholder {emojiShortName} will be substituted with the short name (e.g. ":smile:") of the replacement emoji.'
22
22
  },
23
23
  deleteEmojiLabel: {
24
24
  id: 'fabric.emoji.delete.label',
25
25
  defaultMessage: 'Remove',
26
- description: 'Button label to remove emoji'
26
+ description: 'Label for the primary action button in the emoji removal confirmation dialog that confirms and executes the removal.'
27
27
  },
28
28
  addCustomEmojiLabel: {
29
29
  id: 'fabric.emoji.add.custom.emoji.label',
30
30
  defaultMessage: 'Add your own emoji',
31
- description: 'Button label to add custom emoji'
31
+ description: 'Label for the button in the emoji picker that opens the custom emoji upload panel, allowing users to add their own emoji.'
32
32
  },
33
33
  emojiPlaceholder: {
34
34
  id: 'fabric.emoji.placeholder',
@@ -43,7 +43,7 @@ export const messages = defineMessages({
43
43
  emojiChooseFileTitle: {
44
44
  id: 'fabric.emoji.choose.file.title',
45
45
  defaultMessage: 'Choose file',
46
- description: 'Choose custom emoji file'
46
+ description: 'Label for the file chooser button in the custom emoji upload panel where users select an image file for their emoji.'
47
47
  },
48
48
  emojiChooseFileScreenReaderDescription: {
49
49
  id: 'fabric.emoji.choose.file.screenReaderDescription',
@@ -68,37 +68,37 @@ export const messages = defineMessages({
68
68
  emojiPreviewTitle: {
69
69
  id: 'fabric.emoji.preview.title',
70
70
  defaultMessage: 'Preview',
71
- description: 'Emoji preview title'
71
+ description: 'Section heading shown above the emoji preview area in the custom emoji upload panel.'
72
72
  },
73
73
  emojiPreview: {
74
74
  id: 'fabric.emoji.preview',
75
75
  defaultMessage: 'Your new emoji {emoji} looks great',
76
- description: 'Emoji preview'
76
+ description: 'Success text shown in the emoji preview area after the user uploads a new emoji image. The placeholder {emoji} will be substituted with the rendered emoji image.'
77
77
  },
78
78
  addEmojiLabel: {
79
79
  id: 'fabric.emoji.add.label',
80
80
  defaultMessage: 'Add emoji',
81
- description: 'verb - Button label to add emoji'
81
+ description: 'Label for the submit button in the custom emoji upload panel that saves the new emoji to the workspace.'
82
82
  },
83
83
  retryLabel: {
84
84
  id: 'fabric.emoji.retry.label',
85
85
  defaultMessage: 'Retry',
86
- description: 'verb - Button label to retry upload'
86
+ description: 'Label for the button in the custom emoji upload panel that retries a previously failed upload attempt.'
87
87
  },
88
88
  cancelLabel: {
89
89
  id: 'fabric.emoji.cancel.label',
90
90
  defaultMessage: 'Cancel',
91
- description: 'verb - button label to cancel operation'
91
+ description: 'Label for the cancel button in the custom emoji upload panel that dismisses the panel without saving.'
92
92
  },
93
93
  searchPlaceholder: {
94
94
  id: 'fabric.emoji.search.placeholder',
95
95
  defaultMessage: 'Search',
96
- description: 'Placeholder for search emoji field'
96
+ description: 'Placeholder text shown inside the emoji search input field in the emoji picker before the user types a query.'
97
97
  },
98
98
  searchLabel: {
99
99
  id: 'fabric.emoji.search.label',
100
100
  defaultMessage: 'Emoji name',
101
- description: 'verb - button label to search'
101
+ description: 'Accessible label for the emoji name search input field in the emoji picker, used by screen readers.'
102
102
  },
103
103
  searchResultsStatus: {
104
104
  id: 'fabric.emoji.search.status.count',
@@ -118,67 +118,67 @@ export const messages = defineMessages({
118
118
  categoriesSearchResults: {
119
119
  id: 'fabric.emoji.categories.search.results',
120
120
  defaultMessage: 'Search results',
121
- description: 'Emoji categories search results'
121
+ description: 'Category heading label shown in the emoji picker category bar when displaying results from a search query.'
122
122
  },
123
123
  frequentCategory: {
124
124
  id: 'fabric.emoji.category.frequent',
125
125
  defaultMessage: 'Frequent',
126
- description: 'Emoji frequent category'
126
+ description: 'Label for the Frequent category tab in the emoji picker, showing recently used emojis.'
127
127
  },
128
128
  peopleCategory: {
129
129
  id: 'fabric.emoji.category.people',
130
130
  defaultMessage: 'People',
131
- description: 'Emoji frequent category'
131
+ description: 'Label for the People category tab in the emoji picker, showing face and person emojis.'
132
132
  },
133
133
  natureCategory: {
134
134
  id: 'fabric.emoji.category.nature',
135
135
  defaultMessage: 'Nature',
136
- description: 'Emoji nature category'
136
+ description: 'Label for the Nature category tab in the emoji picker, showing animal and nature emojis.'
137
137
  },
138
138
  foodsCategory: {
139
139
  id: 'fabric.emoji.category.foods',
140
140
  defaultMessage: 'Food & Drink',
141
- description: 'Emoji Foods category'
141
+ description: 'Label for the Food and Drink category tab in the emoji picker.'
142
142
  },
143
143
  activityCategory: {
144
144
  id: 'fabric.emoji.category.activity',
145
145
  defaultMessage: 'Activity',
146
- description: 'Emoji activity category'
146
+ description: 'Label for the Activity category tab in the emoji picker, showing sports and activity emojis.'
147
147
  },
148
148
  placesCategory: {
149
149
  id: 'fabric.emoji.category.places',
150
150
  defaultMessage: 'Travel & Places',
151
- description: 'Emoji Places category'
151
+ description: 'Label for the Travel and Places category tab in the emoji picker, showing location and travel emojis.'
152
152
  },
153
153
  objectsCategory: {
154
154
  id: 'fabric.emoji.category.objects',
155
155
  defaultMessage: 'Objects',
156
- description: 'Emoji objects category'
156
+ description: 'Label for the Objects category tab in the emoji picker, showing everyday object emojis.'
157
157
  },
158
158
  symbolsCategory: {
159
159
  id: 'fabric.emoji.category.symbols',
160
160
  defaultMessage: 'Symbols',
161
- description: 'Emoji symbols category'
161
+ description: 'Label for the Symbols category tab in the emoji picker, showing symbol and sign emojis.'
162
162
  },
163
163
  flagsCategory: {
164
164
  id: 'fabric.emoji.category.flags',
165
165
  defaultMessage: 'Flags',
166
- description: 'Emoji flags category'
166
+ description: 'Label for the Flags category tab in the emoji picker, showing country and regional flag emojis.'
167
167
  },
168
168
  productivityCategory: {
169
169
  id: 'fabric.emoji.category.productivity',
170
170
  defaultMessage: 'Atlassian & productivity',
171
- description: 'Emoji Atlassian & productivity category'
171
+ description: 'Label for the Atlassian and productivity category tab in the emoji picker, showing Atlassian product and productivity emojis.'
172
172
  },
173
173
  userUploadsCustomCategory: {
174
174
  id: 'fabric.emoji.category.user.uploads',
175
175
  defaultMessage: 'Your uploads',
176
- description: 'User uploads in the custom category'
176
+ description: 'Label for the Your Uploads category tab in the emoji picker, showing custom emojis uploaded by the current user.'
177
177
  },
178
178
  allUploadsCustomCategory: {
179
179
  id: 'fabric.emoji.category.all.uploads',
180
180
  defaultMessage: 'All uploads',
181
- description: 'All uploads in the custom category'
181
+ description: 'Label for the All Uploads category tab in the emoji picker, showing all custom emojis uploaded across the workspace.'
182
182
  },
183
183
  deleteEmojiFailed: {
184
184
  id: 'fabric.emoji.error.delete.failed',
@@ -193,12 +193,12 @@ export const messages = defineMessages({
193
193
  emojiUploadFailed: {
194
194
  id: 'fabric.emoji.error.upload.failed',
195
195
  defaultMessage: 'Upload failed',
196
- description: 'Failed to upload emoji image'
196
+ description: 'Error message shown in the custom emoji upload panel when the image upload fails due to an error.'
197
197
  },
198
198
  emojiUploadTimeout: {
199
199
  id: 'fabric.emoji.error.upload.timeout',
200
200
  defaultMessage: 'Upload timed out',
201
- description: 'Failed to upload emoji image'
201
+ description: 'Error message shown in the custom emoji upload panel when the image upload fails because it timed out.'
202
202
  },
203
203
  emojiImageTooBig: {
204
204
  id: 'fabric.emoji.error.image.too.big',
@@ -208,7 +208,7 @@ export const messages = defineMessages({
208
208
  emojiPickerTitle: {
209
209
  id: 'fabric.emoji.picker',
210
210
  defaultMessage: 'Emoji picker',
211
- description: 'Aria label for emoji picker'
211
+ description: 'Accessible aria-label for the emoji picker dialog, used by screen readers to identify the picker.'
212
212
  },
213
213
  emojiPickerListPanel: {
214
214
  id: 'fabric.emoji.pickerlist.tabpanel',
@@ -9,7 +9,7 @@ const createEvent = (eventType, action, actionSubject, actionSubjectId, attribut
9
9
  actionSubjectId,
10
10
  attributes: {
11
11
  packageName: "@atlaskit/emoji",
12
- packageVersion: "70.2.7",
12
+ packageVersion: "70.3.0",
13
13
  ...attributes
14
14
  }
15
15
  });
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Converts an emoji ID (a hyphen-separated string of hex Unicode code points,
3
+ * e.g. `"1f44d"`, `"1f44d-1f3fb"`, `"1f468-200d-1f469-200d-1f467"`) into its
4
+ * corresponding Unicode string.
5
+ *
6
+ * Special handling:
7
+ * - Keycap sequences: U+20E3 (COMBINING ENCLOSING KEYCAP) must be preceded by
8
+ * U+FE0F (VARIATION SELECTOR-16) to form a well-defined keycap emoji.
9
+ * - Text-default emoji (characters whose default presentation is text) must be
10
+ * followed by U+FE0F to force emoji presentation.
11
+ *
12
+ * @returns The Unicode string for the emoji, or `undefined` if the input is
13
+ * invalid (empty string, non-hex segments, or out-of-range code points).
14
+ */
15
+ export function emojiIdToEmoji(id) {
16
+ if (!id) {
17
+ return undefined;
18
+ }
19
+ const segments = id.split('-');
20
+ const codePoints = [];
21
+ for (const segment of segments) {
22
+ if (!/^[0-9a-fA-F]+$/.test(segment)) {
23
+ return undefined;
24
+ }
25
+ const cp = parseInt(segment, 16);
26
+ // Valid Unicode scalar values: 0x0000–0x10FFFF, excluding surrogates 0xD800–0xDFFF
27
+ if (isNaN(cp) || cp < 0 || cp > 0x10ffff || cp >= 0xd800 && cp <= 0xdfff) {
28
+ return undefined;
29
+ }
30
+ codePoints.push(cp);
31
+ }
32
+ if (codePoints.length === 0) {
33
+ return undefined;
34
+ }
35
+
36
+ // Keycap sequences: digit/# /* followed by U+20E3 need U+FE0F between them.
37
+ // The keycap base characters are: 0x23 (#), 0x2A (*), 0x30–0x39 (0–9).
38
+ const KEYCAP_COMBINING = 0x20e3;
39
+ const VARIATION_SELECTOR_16 = 0xfe0f;
40
+ const processedCodePoints = [];
41
+ for (let i = 0; i < codePoints.length; i++) {
42
+ const cp = codePoints[i];
43
+ const next = codePoints[i + 1];
44
+ processedCodePoints.push(cp);
45
+
46
+ // Insert U+FE0F before U+20E3 if not already present
47
+ if (next === KEYCAP_COMBINING && cp !== VARIATION_SELECTOR_16) {
48
+ processedCodePoints.push(VARIATION_SELECTOR_16);
49
+ }
50
+ }
51
+ try {
52
+ return String.fromCodePoint(...processedCodePoints);
53
+ } catch {
54
+ return undefined;
55
+ }
56
+ }
@@ -572,6 +572,34 @@ export var EmojiResource = /*#__PURE__*/function (_ref) {
572
572
  return _this6.findById(id);
573
573
  }, undefined);
574
574
  }
575
+
576
+ /**
577
+ * Synchronously reads the emoji type from the local cache for the given emojiId.
578
+ * Returns `undefined` when the emoji is not in the cache or no repository is initialised.
579
+ * This is a pure cache read — no network requests are made.
580
+ */
581
+ }, {
582
+ key: "getCachedEmojiType",
583
+ value: function getCachedEmojiType(emojiId) {
584
+ if (!this.isRepositoryAvailable(this.emojiRepository)) {
585
+ return undefined;
586
+ }
587
+ var id = emojiId.id,
588
+ shortName = emojiId.shortName;
589
+ if (id) {
590
+ var emoji = this.emojiRepository.findById(id);
591
+ if (emoji) {
592
+ return emoji.type;
593
+ }
594
+ }
595
+ if (shortName) {
596
+ var _result = this.emojiRepository.findByShortName(shortName);
597
+ if (_result && !('then' in _result)) {
598
+ return _result.type;
599
+ }
600
+ }
601
+ return undefined;
602
+ }
575
603
  }, {
576
604
  key: "findInCategory",
577
605
  value: function findInCategory(categoryId) {
@@ -13,22 +13,22 @@ export var messages = defineMessages({
13
13
  deleteEmojiTitle: {
14
14
  id: 'fabric.emoji.delete.title',
15
15
  defaultMessage: 'Remove emoji',
16
- description: 'Title for emoji removal dialog'
16
+ description: 'Heading text shown at the top of the emoji removal confirmation dialog, prompting the user to confirm removing the selected custom emoji.'
17
17
  },
18
18
  deleteEmojiDescription: {
19
19
  id: 'fabric.emoji.delete.description',
20
20
  defaultMessage: 'All existing instances of this emoji will be replaced with {emojiShortName}',
21
- description: 'Description for emoji removal dialog'
21
+ description: 'Body text shown inside the emoji removal confirmation dialog. The placeholder {emojiShortName} will be substituted with the short name (e.g. ":smile:") of the replacement emoji.'
22
22
  },
23
23
  deleteEmojiLabel: {
24
24
  id: 'fabric.emoji.delete.label',
25
25
  defaultMessage: 'Remove',
26
- description: 'Button label to remove emoji'
26
+ description: 'Label for the primary action button in the emoji removal confirmation dialog that confirms and executes the removal.'
27
27
  },
28
28
  addCustomEmojiLabel: {
29
29
  id: 'fabric.emoji.add.custom.emoji.label',
30
30
  defaultMessage: 'Add your own emoji',
31
- description: 'Button label to add custom emoji'
31
+ description: 'Label for the button in the emoji picker that opens the custom emoji upload panel, allowing users to add their own emoji.'
32
32
  },
33
33
  emojiPlaceholder: {
34
34
  id: 'fabric.emoji.placeholder',
@@ -43,7 +43,7 @@ export var messages = defineMessages({
43
43
  emojiChooseFileTitle: {
44
44
  id: 'fabric.emoji.choose.file.title',
45
45
  defaultMessage: 'Choose file',
46
- description: 'Choose custom emoji file'
46
+ description: 'Label for the file chooser button in the custom emoji upload panel where users select an image file for their emoji.'
47
47
  },
48
48
  emojiChooseFileScreenReaderDescription: {
49
49
  id: 'fabric.emoji.choose.file.screenReaderDescription',
@@ -68,37 +68,37 @@ export var messages = defineMessages({
68
68
  emojiPreviewTitle: {
69
69
  id: 'fabric.emoji.preview.title',
70
70
  defaultMessage: 'Preview',
71
- description: 'Emoji preview title'
71
+ description: 'Section heading shown above the emoji preview area in the custom emoji upload panel.'
72
72
  },
73
73
  emojiPreview: {
74
74
  id: 'fabric.emoji.preview',
75
75
  defaultMessage: 'Your new emoji {emoji} looks great',
76
- description: 'Emoji preview'
76
+ description: 'Success text shown in the emoji preview area after the user uploads a new emoji image. The placeholder {emoji} will be substituted with the rendered emoji image.'
77
77
  },
78
78
  addEmojiLabel: {
79
79
  id: 'fabric.emoji.add.label',
80
80
  defaultMessage: 'Add emoji',
81
- description: 'verb - Button label to add emoji'
81
+ description: 'Label for the submit button in the custom emoji upload panel that saves the new emoji to the workspace.'
82
82
  },
83
83
  retryLabel: {
84
84
  id: 'fabric.emoji.retry.label',
85
85
  defaultMessage: 'Retry',
86
- description: 'verb - Button label to retry upload'
86
+ description: 'Label for the button in the custom emoji upload panel that retries a previously failed upload attempt.'
87
87
  },
88
88
  cancelLabel: {
89
89
  id: 'fabric.emoji.cancel.label',
90
90
  defaultMessage: 'Cancel',
91
- description: 'verb - button label to cancel operation'
91
+ description: 'Label for the cancel button in the custom emoji upload panel that dismisses the panel without saving.'
92
92
  },
93
93
  searchPlaceholder: {
94
94
  id: 'fabric.emoji.search.placeholder',
95
95
  defaultMessage: 'Search',
96
- description: 'Placeholder for search emoji field'
96
+ description: 'Placeholder text shown inside the emoji search input field in the emoji picker before the user types a query.'
97
97
  },
98
98
  searchLabel: {
99
99
  id: 'fabric.emoji.search.label',
100
100
  defaultMessage: 'Emoji name',
101
- description: 'verb - button label to search'
101
+ description: 'Accessible label for the emoji name search input field in the emoji picker, used by screen readers.'
102
102
  },
103
103
  searchResultsStatus: {
104
104
  id: 'fabric.emoji.search.status.count',
@@ -118,67 +118,67 @@ export var messages = defineMessages({
118
118
  categoriesSearchResults: {
119
119
  id: 'fabric.emoji.categories.search.results',
120
120
  defaultMessage: 'Search results',
121
- description: 'Emoji categories search results'
121
+ description: 'Category heading label shown in the emoji picker category bar when displaying results from a search query.'
122
122
  },
123
123
  frequentCategory: {
124
124
  id: 'fabric.emoji.category.frequent',
125
125
  defaultMessage: 'Frequent',
126
- description: 'Emoji frequent category'
126
+ description: 'Label for the Frequent category tab in the emoji picker, showing recently used emojis.'
127
127
  },
128
128
  peopleCategory: {
129
129
  id: 'fabric.emoji.category.people',
130
130
  defaultMessage: 'People',
131
- description: 'Emoji frequent category'
131
+ description: 'Label for the People category tab in the emoji picker, showing face and person emojis.'
132
132
  },
133
133
  natureCategory: {
134
134
  id: 'fabric.emoji.category.nature',
135
135
  defaultMessage: 'Nature',
136
- description: 'Emoji nature category'
136
+ description: 'Label for the Nature category tab in the emoji picker, showing animal and nature emojis.'
137
137
  },
138
138
  foodsCategory: {
139
139
  id: 'fabric.emoji.category.foods',
140
140
  defaultMessage: 'Food & Drink',
141
- description: 'Emoji Foods category'
141
+ description: 'Label for the Food and Drink category tab in the emoji picker.'
142
142
  },
143
143
  activityCategory: {
144
144
  id: 'fabric.emoji.category.activity',
145
145
  defaultMessage: 'Activity',
146
- description: 'Emoji activity category'
146
+ description: 'Label for the Activity category tab in the emoji picker, showing sports and activity emojis.'
147
147
  },
148
148
  placesCategory: {
149
149
  id: 'fabric.emoji.category.places',
150
150
  defaultMessage: 'Travel & Places',
151
- description: 'Emoji Places category'
151
+ description: 'Label for the Travel and Places category tab in the emoji picker, showing location and travel emojis.'
152
152
  },
153
153
  objectsCategory: {
154
154
  id: 'fabric.emoji.category.objects',
155
155
  defaultMessage: 'Objects',
156
- description: 'Emoji objects category'
156
+ description: 'Label for the Objects category tab in the emoji picker, showing everyday object emojis.'
157
157
  },
158
158
  symbolsCategory: {
159
159
  id: 'fabric.emoji.category.symbols',
160
160
  defaultMessage: 'Symbols',
161
- description: 'Emoji symbols category'
161
+ description: 'Label for the Symbols category tab in the emoji picker, showing symbol and sign emojis.'
162
162
  },
163
163
  flagsCategory: {
164
164
  id: 'fabric.emoji.category.flags',
165
165
  defaultMessage: 'Flags',
166
- description: 'Emoji flags category'
166
+ description: 'Label for the Flags category tab in the emoji picker, showing country and regional flag emojis.'
167
167
  },
168
168
  productivityCategory: {
169
169
  id: 'fabric.emoji.category.productivity',
170
170
  defaultMessage: 'Atlassian & productivity',
171
- description: 'Emoji Atlassian & productivity category'
171
+ description: 'Label for the Atlassian and productivity category tab in the emoji picker, showing Atlassian product and productivity emojis.'
172
172
  },
173
173
  userUploadsCustomCategory: {
174
174
  id: 'fabric.emoji.category.user.uploads',
175
175
  defaultMessage: 'Your uploads',
176
- description: 'User uploads in the custom category'
176
+ description: 'Label for the Your Uploads category tab in the emoji picker, showing custom emojis uploaded by the current user.'
177
177
  },
178
178
  allUploadsCustomCategory: {
179
179
  id: 'fabric.emoji.category.all.uploads',
180
180
  defaultMessage: 'All uploads',
181
- description: 'All uploads in the custom category'
181
+ description: 'Label for the All Uploads category tab in the emoji picker, showing all custom emojis uploaded across the workspace.'
182
182
  },
183
183
  deleteEmojiFailed: {
184
184
  id: 'fabric.emoji.error.delete.failed',
@@ -193,12 +193,12 @@ export var messages = defineMessages({
193
193
  emojiUploadFailed: {
194
194
  id: 'fabric.emoji.error.upload.failed',
195
195
  defaultMessage: 'Upload failed',
196
- description: 'Failed to upload emoji image'
196
+ description: 'Error message shown in the custom emoji upload panel when the image upload fails due to an error.'
197
197
  },
198
198
  emojiUploadTimeout: {
199
199
  id: 'fabric.emoji.error.upload.timeout',
200
200
  defaultMessage: 'Upload timed out',
201
- description: 'Failed to upload emoji image'
201
+ description: 'Error message shown in the custom emoji upload panel when the image upload fails because it timed out.'
202
202
  },
203
203
  emojiImageTooBig: {
204
204
  id: 'fabric.emoji.error.image.too.big',
@@ -208,7 +208,7 @@ export var messages = defineMessages({
208
208
  emojiPickerTitle: {
209
209
  id: 'fabric.emoji.picker',
210
210
  defaultMessage: 'Emoji picker',
211
- description: 'Aria label for emoji picker'
211
+ description: 'Accessible aria-label for the emoji picker dialog, used by screen readers to identify the picker.'
212
212
  },
213
213
  emojiPickerListPanel: {
214
214
  id: 'fabric.emoji.pickerlist.tabpanel',
@@ -14,7 +14,7 @@ var createEvent = function createEvent(eventType, action, actionSubject, actionS
14
14
  actionSubjectId: actionSubjectId,
15
15
  attributes: _objectSpread({
16
16
  packageName: "@atlaskit/emoji",
17
- packageVersion: "70.2.7"
17
+ packageVersion: "70.3.0"
18
18
  }, attributes)
19
19
  };
20
20
  };
@@ -0,0 +1,68 @@
1
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
2
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
3
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
4
+ /**
5
+ * Converts an emoji ID (a hyphen-separated string of hex Unicode code points,
6
+ * e.g. `"1f44d"`, `"1f44d-1f3fb"`, `"1f468-200d-1f469-200d-1f467"`) into its
7
+ * corresponding Unicode string.
8
+ *
9
+ * Special handling:
10
+ * - Keycap sequences: U+20E3 (COMBINING ENCLOSING KEYCAP) must be preceded by
11
+ * U+FE0F (VARIATION SELECTOR-16) to form a well-defined keycap emoji.
12
+ * - Text-default emoji (characters whose default presentation is text) must be
13
+ * followed by U+FE0F to force emoji presentation.
14
+ *
15
+ * @returns The Unicode string for the emoji, or `undefined` if the input is
16
+ * invalid (empty string, non-hex segments, or out-of-range code points).
17
+ */
18
+ export function emojiIdToEmoji(id) {
19
+ if (!id) {
20
+ return undefined;
21
+ }
22
+ var segments = id.split('-');
23
+ var codePoints = [];
24
+ var _iterator = _createForOfIteratorHelper(segments),
25
+ _step;
26
+ try {
27
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
28
+ var segment = _step.value;
29
+ if (!/^[0-9a-fA-F]+$/.test(segment)) {
30
+ return undefined;
31
+ }
32
+ var _cp = parseInt(segment, 16);
33
+ // Valid Unicode scalar values: 0x0000–0x10FFFF, excluding surrogates 0xD800–0xDFFF
34
+ if (isNaN(_cp) || _cp < 0 || _cp > 0x10ffff || _cp >= 0xd800 && _cp <= 0xdfff) {
35
+ return undefined;
36
+ }
37
+ codePoints.push(_cp);
38
+ }
39
+ } catch (err) {
40
+ _iterator.e(err);
41
+ } finally {
42
+ _iterator.f();
43
+ }
44
+ if (codePoints.length === 0) {
45
+ return undefined;
46
+ }
47
+
48
+ // Keycap sequences: digit/# /* followed by U+20E3 need U+FE0F between them.
49
+ // The keycap base characters are: 0x23 (#), 0x2A (*), 0x30–0x39 (0–9).
50
+ var KEYCAP_COMBINING = 0x20e3;
51
+ var VARIATION_SELECTOR_16 = 0xfe0f;
52
+ var processedCodePoints = [];
53
+ for (var i = 0; i < codePoints.length; i++) {
54
+ var cp = codePoints[i];
55
+ var next = codePoints[i + 1];
56
+ processedCodePoints.push(cp);
57
+
58
+ // Insert U+FE0F before U+20E3 if not already present
59
+ if (next === KEYCAP_COMBINING && cp !== VARIATION_SELECTOR_16) {
60
+ processedCodePoints.push(VARIATION_SELECTOR_16);
61
+ }
62
+ }
63
+ try {
64
+ return String.fromCodePoint.apply(String, processedCodePoints);
65
+ } catch (_unused) {
66
+ return undefined;
67
+ }
68
+ }
@@ -127,6 +127,12 @@ export declare class EmojiResource extends AbstractResource<string, EmojiSearchR
127
127
  findByShortName(shortName: string): OptionalEmojiDescription | Promise<OptionalEmojiDescription>;
128
128
  findByEmojiId(emojiId: EmojiId): OptionalEmojiDescription | Promise<OptionalEmojiDescription>;
129
129
  findById(id: string): OptionalEmojiDescription | Promise<OptionalEmojiDescription>;
130
+ /**
131
+ * Synchronously reads the emoji type from the local cache for the given emojiId.
132
+ * Returns `undefined` when the emoji is not in the cache or no repository is initialised.
133
+ * This is a pure cache read — no network requests are made.
134
+ */
135
+ getCachedEmojiType(emojiId: EmojiId): string | undefined;
130
136
  findInCategory(categoryId: CategoryId): Promise<EmojiDescription[]>;
131
137
  getAsciiMap(): Promise<Map<string, EmojiDescription>>;
132
138
  getFrequentlyUsed(options?: SearchOptions): Promise<EmojiDescription[]>;
@@ -81,6 +81,17 @@ export interface EmojiProvider extends Provider<string, EmojiSearchResult, any,
81
81
  * Returns a constructed URL to fetch emoji media asset if 'optimisticImageApi' config has been provided
82
82
  */
83
83
  getOptimisticImageURL(emojiId: EmojiId): string | undefined;
84
+ /**
85
+ * Synchronously reads the emoji type (e.g. 'STANDARD', 'SITE', 'ATLASSIAN') from the
86
+ * local cache for the given emojiId, without making any network requests.
87
+ *
88
+ * Returns `undefined` when the emoji is not yet in the cache or no repository has been
89
+ * initialised. This is an optional method — callers must fall back gracefully when it is
90
+ * absent (e.g. in mock or legacy providers).
91
+ *
92
+ * Optional.
93
+ */
94
+ getCachedEmojiType?(emojiId: EmojiId): string | undefined;
84
95
  /**
85
96
  * Used by the picker and typeahead to obtain a skin tone preference
86
97
  * if the user has previously selected one via the Tone Selector
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Converts an emoji ID (a hyphen-separated string of hex Unicode code points,
3
+ * e.g. `"1f44d"`, `"1f44d-1f3fb"`, `"1f468-200d-1f469-200d-1f467"`) into its
4
+ * corresponding Unicode string.
5
+ *
6
+ * Special handling:
7
+ * - Keycap sequences: U+20E3 (COMBINING ENCLOSING KEYCAP) must be preceded by
8
+ * U+FE0F (VARIATION SELECTOR-16) to form a well-defined keycap emoji.
9
+ * - Text-default emoji (characters whose default presentation is text) must be
10
+ * followed by U+FE0F to force emoji presentation.
11
+ *
12
+ * @returns The Unicode string for the emoji, or `undefined` if the input is
13
+ * invalid (empty string, non-hex segments, or out-of-range code points).
14
+ */
15
+ export declare function emojiIdToEmoji(id: string): string | undefined;
@@ -127,6 +127,12 @@ export declare class EmojiResource extends AbstractResource<string, EmojiSearchR
127
127
  findByShortName(shortName: string): OptionalEmojiDescription | Promise<OptionalEmojiDescription>;
128
128
  findByEmojiId(emojiId: EmojiId): OptionalEmojiDescription | Promise<OptionalEmojiDescription>;
129
129
  findById(id: string): OptionalEmojiDescription | Promise<OptionalEmojiDescription>;
130
+ /**
131
+ * Synchronously reads the emoji type from the local cache for the given emojiId.
132
+ * Returns `undefined` when the emoji is not in the cache or no repository is initialised.
133
+ * This is a pure cache read — no network requests are made.
134
+ */
135
+ getCachedEmojiType(emojiId: EmojiId): string | undefined;
130
136
  findInCategory(categoryId: CategoryId): Promise<EmojiDescription[]>;
131
137
  getAsciiMap(): Promise<Map<string, EmojiDescription>>;
132
138
  getFrequentlyUsed(options?: SearchOptions): Promise<EmojiDescription[]>;
@@ -81,6 +81,17 @@ export interface EmojiProvider extends Provider<string, EmojiSearchResult, any,
81
81
  * Returns a constructed URL to fetch emoji media asset if 'optimisticImageApi' config has been provided
82
82
  */
83
83
  getOptimisticImageURL(emojiId: EmojiId): string | undefined;
84
+ /**
85
+ * Synchronously reads the emoji type (e.g. 'STANDARD', 'SITE', 'ATLASSIAN') from the
86
+ * local cache for the given emojiId, without making any network requests.
87
+ *
88
+ * Returns `undefined` when the emoji is not yet in the cache or no repository has been
89
+ * initialised. This is an optional method — callers must fall back gracefully when it is
90
+ * absent (e.g. in mock or legacy providers).
91
+ *
92
+ * Optional.
93
+ */
94
+ getCachedEmojiType?(emojiId: EmojiId): string | undefined;
84
95
  /**
85
96
  * Used by the picker and typeahead to obtain a skin tone preference
86
97
  * if the user has previously selected one via the Tone Selector
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Converts an emoji ID (a hyphen-separated string of hex Unicode code points,
3
+ * e.g. `"1f44d"`, `"1f44d-1f3fb"`, `"1f468-200d-1f469-200d-1f467"`) into its
4
+ * corresponding Unicode string.
5
+ *
6
+ * Special handling:
7
+ * - Keycap sequences: U+20E3 (COMBINING ENCLOSING KEYCAP) must be preceded by
8
+ * U+FE0F (VARIATION SELECTOR-16) to form a well-defined keycap emoji.
9
+ * - Text-default emoji (characters whose default presentation is text) must be
10
+ * followed by U+FE0F to force emoji presentation.
11
+ *
12
+ * @returns The Unicode string for the emoji, or `undefined` if the input is
13
+ * invalid (empty string, non-hex segments, or out-of-range code points).
14
+ */
15
+ export declare function emojiIdToEmoji(id: string): string | undefined;
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "@atlaskit/emoji/emoji-id-to-emoji",
3
+ "main": "../dist/cjs/util/emojiIdToEmoji.js",
4
+ "module": "../dist/esm/util/emojiIdToEmoji.js",
5
+ "module:es2019": "../dist/es2019/util/emojiIdToEmoji.js",
6
+ "sideEffects": [
7
+ "*.compiled.css"
8
+ ],
9
+ "types": "../dist/types/util/emojiIdToEmoji.d.ts",
10
+ "typesVersions": {
11
+ ">=4.5 <5.9": {
12
+ "*": [
13
+ "../dist/types-ts4.5/util/emojiIdToEmoji.d.ts"
14
+ ]
15
+ }
16
+ }
17
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/emoji",
3
- "version": "70.2.8",
3
+ "version": "70.3.1",
4
4
  "description": "Fabric emoji React components",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -49,12 +49,12 @@
49
49
  "@atlaskit/primitives": "^19.0.0",
50
50
  "@atlaskit/spinner": "^19.1.0",
51
51
  "@atlaskit/textfield": "^8.3.0",
52
- "@atlaskit/tmp-editor-statsig": "^74.0.0",
52
+ "@atlaskit/tmp-editor-statsig": "^74.8.0",
53
53
  "@atlaskit/tokens": "^13.0.0",
54
- "@atlaskit/tooltip": "^21.2.0",
54
+ "@atlaskit/tooltip": "^22.0.0",
55
55
  "@atlaskit/ufo": "^0.4.0",
56
56
  "@atlaskit/util-service-support": "^6.3.0",
57
- "@atlaskit/visually-hidden": "^3.0.0",
57
+ "@atlaskit/visually-hidden": "^3.1.0",
58
58
  "@babel/runtime": "^7.0.0",
59
59
  "@compiled/react": "^0.20.0",
60
60
  "@tanstack/react-virtual": "3.0.0-beta.60",
@@ -116,6 +116,9 @@
116
116
  }
117
117
  },
118
118
  "platform-feature-flags": {
119
+ "platform_twemoji_removal_unicode_emojis": {
120
+ "type": "boolean"
121
+ },
119
122
  "platform_index_emoji_just_in_time": {
120
123
  "type": "boolean"
121
124
  },