emoji-picker-react 4.20.4 → 4.20.6
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/dist/components/body/EmojiCategory.d.ts +2 -1
- package/dist/components/emoji/Emoji.d.ts +2 -1
- package/dist/components/header/SkinTonePicker/BtnSkinToneVariation.d.ts +2 -1
- package/dist/emoji-picker-react.cjs.development.js +158 -32
- package/dist/emoji-picker-react.cjs.development.js.map +1 -1
- package/dist/emoji-picker-react.cjs.production.min.js +1 -1
- package/dist/emoji-picker-react.cjs.production.min.js.map +1 -1
- package/dist/emoji-picker-react.esm.js +158 -32
- package/dist/emoji-picker-react.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/DomUtils/elementPositionInRow.ts +11 -0
- package/src/components/Reactions/Reactions.tsx +20 -0
- package/src/components/body/EmojiCategory.tsx +9 -1
- package/src/components/body/EmojiList.tsx +25 -3
- package/src/components/emoji/Emoji.tsx +3 -0
- package/src/components/header/SkinTonePicker/BtnSkinToneVariation.tsx +3 -0
- package/src/components/header/SkinTonePicker/SkinTonePicker.tsx +6 -0
- package/src/hooks/useKeyboardNavigation.ts +81 -1
|
@@ -6,6 +6,7 @@ declare type Props = Readonly<{
|
|
|
6
6
|
hidden?: boolean;
|
|
7
7
|
hiddenOnSearch?: boolean;
|
|
8
8
|
height?: number;
|
|
9
|
+
emojisPerRow?: number;
|
|
9
10
|
}>;
|
|
10
|
-
export declare function EmojiCategory({ categoryConfig, children, hidden, hiddenOnSearch, height, }: Props): JSX.Element;
|
|
11
|
+
export declare function EmojiCategory({ categoryConfig, children, hidden, hiddenOnSearch, height, emojisPerRow, }: Props): JSX.Element;
|
|
11
12
|
export {};
|
|
@@ -7,8 +7,9 @@ declare type ClickableEmojiProps = Readonly<BaseEmojiProps & {
|
|
|
7
7
|
hiddenOnSearch?: boolean;
|
|
8
8
|
emoji: DataEmoji;
|
|
9
9
|
className?: string;
|
|
10
|
+
buttonClassName?: string;
|
|
10
11
|
noBackground?: boolean;
|
|
11
12
|
style?: React.CSSProperties;
|
|
12
13
|
}>;
|
|
13
|
-
export declare function ClickableEmoji({ emoji, unified, hidden, hiddenOnSearch, emojiStyle, showVariations, size, lazyLoad, getEmojiUrl, className, noBackground, style, }: ClickableEmojiProps): JSX.Element;
|
|
14
|
+
export declare function ClickableEmoji({ emoji, unified, hidden, hiddenOnSearch, emojiStyle, showVariations, size, lazyLoad, getEmojiUrl, className, buttonClassName, noBackground, style, }: ClickableEmojiProps): JSX.Element;
|
|
14
15
|
export {};
|
|
@@ -6,6 +6,7 @@ declare type Props = {
|
|
|
6
6
|
isActive: boolean;
|
|
7
7
|
skinToneVariation: SkinTones;
|
|
8
8
|
style?: React.CSSProperties;
|
|
9
|
+
tabIndex?: number;
|
|
9
10
|
};
|
|
10
|
-
export declare function BtnSkinToneVariation({ isOpen, onClick, isActive, skinToneVariation, style, }: Props): JSX.Element;
|
|
11
|
+
export declare function BtnSkinToneVariation({ isOpen, onClick, isActive, skinToneVariation, style, tabIndex, }: Props): JSX.Element;
|
|
11
12
|
export {};
|
|
@@ -27900,6 +27900,7 @@ function useKeyboardNavigation() {
|
|
|
27900
27900
|
useSearchInputKeyboardEvents();
|
|
27901
27901
|
useSkinTonePickerKeyboardEvents();
|
|
27902
27902
|
useCategoryNavigationKeyboardEvents();
|
|
27903
|
+
useReactionsKeyboardEvents();
|
|
27903
27904
|
useBodyKeyboardEvents();
|
|
27904
27905
|
}
|
|
27905
27906
|
function usePickerMainKeyboardEvents() {
|
|
@@ -28105,6 +28106,65 @@ function useCategoryNavigationKeyboardEvents() {
|
|
|
28105
28106
|
};
|
|
28106
28107
|
}, [CategoryNavigationRef, BodyRef, onKeyDown]);
|
|
28107
28108
|
}
|
|
28109
|
+
// Arrow keys move between reaction buttons (and the expand button)
|
|
28110
|
+
// while the reactions bar has focus. The bar lives outside the scroll
|
|
28111
|
+
// body, so the body handler never sees these events.
|
|
28112
|
+
// https://github.com/ealush/emoji-picker-react/issues/411
|
|
28113
|
+
function useReactionsKeyboardEvents() {
|
|
28114
|
+
var ReactionsRef = useReactionsRef();
|
|
28115
|
+
// The reactions bar mounts late when the user collapses the full
|
|
28116
|
+
// picker, so the listener effect must rerun when it opens.
|
|
28117
|
+
var _useReactionsModeStat = useReactionsModeState(),
|
|
28118
|
+
reactionsOpen = _useReactionsModeStat[0];
|
|
28119
|
+
var onKeyDown = React.useMemo(function () {
|
|
28120
|
+
return function onKeyDown(event) {
|
|
28121
|
+
var key = event.key;
|
|
28122
|
+
var current = ReactionsRef.current;
|
|
28123
|
+
if (!current) {
|
|
28124
|
+
return;
|
|
28125
|
+
}
|
|
28126
|
+
var delta = reactionFocusDelta(key);
|
|
28127
|
+
if (delta !== 0 && focusReactionSibling(current, delta)) {
|
|
28128
|
+
event.preventDefault();
|
|
28129
|
+
}
|
|
28130
|
+
};
|
|
28131
|
+
},
|
|
28132
|
+
// reactionsOpen: the bar mounts late on collapse, and the new
|
|
28133
|
+
// callback identity reinstalls the listener effect below.
|
|
28134
|
+
[ReactionsRef, reactionsOpen]);
|
|
28135
|
+
React.useEffect(function () {
|
|
28136
|
+
var current = ReactionsRef.current;
|
|
28137
|
+
if (!current) {
|
|
28138
|
+
return;
|
|
28139
|
+
}
|
|
28140
|
+
current.addEventListener('keydown', onKeyDown);
|
|
28141
|
+
return function () {
|
|
28142
|
+
current.removeEventListener('keydown', onKeyDown);
|
|
28143
|
+
};
|
|
28144
|
+
}, [ReactionsRef, onKeyDown]);
|
|
28145
|
+
}
|
|
28146
|
+
function reactionFocusDelta(key) {
|
|
28147
|
+
switch (key) {
|
|
28148
|
+
case KeyboardEvents.ArrowRight:
|
|
28149
|
+
case KeyboardEvents.ArrowDown:
|
|
28150
|
+
return 1;
|
|
28151
|
+
case KeyboardEvents.ArrowLeft:
|
|
28152
|
+
case KeyboardEvents.ArrowUp:
|
|
28153
|
+
return -1;
|
|
28154
|
+
default:
|
|
28155
|
+
return 0;
|
|
28156
|
+
}
|
|
28157
|
+
}
|
|
28158
|
+
function focusReactionSibling(root, delta) {
|
|
28159
|
+
var buttons = Array.from(root.querySelectorAll('button'));
|
|
28160
|
+
var activeIndex = buttons.indexOf(getActiveElement());
|
|
28161
|
+
var sibling = activeIndex === -1 ? null : buttons[activeIndex + delta];
|
|
28162
|
+
if (!sibling) {
|
|
28163
|
+
return false;
|
|
28164
|
+
}
|
|
28165
|
+
focusElement(sibling);
|
|
28166
|
+
return true;
|
|
28167
|
+
}
|
|
28108
28168
|
function useBodyKeyboardEvents() {
|
|
28109
28169
|
var BodyRef = useBodyRef();
|
|
28110
28170
|
var goUpFromBody = useGoUpFromBody();
|
|
@@ -28480,9 +28540,20 @@ var styles = /*#__PURE__*/stylesheet.create({
|
|
|
28480
28540
|
});
|
|
28481
28541
|
|
|
28482
28542
|
function elementCountInRow(parent, element) {
|
|
28543
|
+
var _parent$dataset;
|
|
28483
28544
|
if (!parent || !element) {
|
|
28484
28545
|
return 0;
|
|
28485
28546
|
}
|
|
28547
|
+
// The virtualized layout positions emojis with a fixed per-row count that
|
|
28548
|
+
// is rendered onto the category content element. That count is the single
|
|
28549
|
+
// source of truth: deriving it again from fractional rect widths can be
|
|
28550
|
+
// off by one (e.g. category padding shrinking the content box), which
|
|
28551
|
+
// makes arrow-up/arrow-down drift sideways by a column.
|
|
28552
|
+
// https://github.com/ealush/emoji-picker-react/issues/502
|
|
28553
|
+
var declared = Number((_parent$dataset = parent.dataset) == null ? void 0 : _parent$dataset.emojisPerRow);
|
|
28554
|
+
if (Number.isInteger(declared) && declared > 0) {
|
|
28555
|
+
return declared;
|
|
28556
|
+
}
|
|
28486
28557
|
var parentWidth = parent.getBoundingClientRect().width;
|
|
28487
28558
|
var elementWidth = element.getBoundingClientRect().width;
|
|
28488
28559
|
return Math.floor(parentWidth / elementWidth);
|
|
@@ -29120,6 +29191,7 @@ function ClickableEmoji(_ref) {
|
|
|
29120
29191
|
lazyLoad = _ref.lazyLoad,
|
|
29121
29192
|
getEmojiUrl = _ref.getEmojiUrl,
|
|
29122
29193
|
className = _ref.className,
|
|
29194
|
+
buttonClassName = _ref.buttonClassName,
|
|
29123
29195
|
_ref$noBackground = _ref.noBackground,
|
|
29124
29196
|
noBackground = _ref$noBackground === void 0 ? false : _ref$noBackground,
|
|
29125
29197
|
style = _ref.style;
|
|
@@ -29132,7 +29204,8 @@ function ClickableEmoji(_ref) {
|
|
|
29132
29204
|
emojiNames: emojiNames(emoji),
|
|
29133
29205
|
unified: unified,
|
|
29134
29206
|
noBackground: noBackground,
|
|
29135
|
-
style: style
|
|
29207
|
+
style: style,
|
|
29208
|
+
className: buttonClassName
|
|
29136
29209
|
}, React.createElement(ViewOnlyEmoji, {
|
|
29137
29210
|
unified: unified,
|
|
29138
29211
|
emoji: emoji,
|
|
@@ -29236,6 +29309,7 @@ function Reactions() {
|
|
|
29236
29309
|
unified: reaction,
|
|
29237
29310
|
showVariations: false,
|
|
29238
29311
|
className: shipstyles.cx(styles$6.emojiButton),
|
|
29312
|
+
buttonClassName: shipstyles.cx(styles$6.reactionButton),
|
|
29239
29313
|
noBackground: true,
|
|
29240
29314
|
getEmojiUrl: getEmojiUrl
|
|
29241
29315
|
}));
|
|
@@ -29251,6 +29325,24 @@ var styles$6 = /*#__PURE__*/stylesheet.create({
|
|
|
29251
29325
|
alignItems: 'center',
|
|
29252
29326
|
height: '100%'
|
|
29253
29327
|
},
|
|
29328
|
+
reactionButton: {
|
|
29329
|
+
// Every picker button sets outline: none, and reaction buttons also
|
|
29330
|
+
// suppress the focus background, so keyboard focus was invisible.
|
|
29331
|
+
// Focus mirrors the hover treatment (scale plus a soft circular
|
|
29332
|
+
// wash, no outline ring): the scale transform on the inner image
|
|
29333
|
+
// alone is clipped by the button's overflow: hidden, and does
|
|
29334
|
+
// nothing on inline native emoji, so the button itself carries the
|
|
29335
|
+
// effect. Scoped to the reactions bar so grid focus treatment is
|
|
29336
|
+
// untouched.
|
|
29337
|
+
// https://github.com/ealush/emoji-picker-react/issues/473
|
|
29338
|
+
':focus-visible': {
|
|
29339
|
+
transform: 'scale(1.2)',
|
|
29340
|
+
transition: 'background-color 0.2s, transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.5)',
|
|
29341
|
+
backgroundColor: 'var(--epr-hover-bg-color-reduced-opacity)',
|
|
29342
|
+
borderRadius: '50%',
|
|
29343
|
+
outline: 'none'
|
|
29344
|
+
}
|
|
29345
|
+
},
|
|
29254
29346
|
emojiButton: {
|
|
29255
29347
|
':hover': {
|
|
29256
29348
|
transform: 'scale(1.2)'
|
|
@@ -29474,12 +29566,14 @@ function EmojiCategory(_ref) {
|
|
|
29474
29566
|
children = _ref.children,
|
|
29475
29567
|
hidden = _ref.hidden,
|
|
29476
29568
|
hiddenOnSearch = _ref.hiddenOnSearch,
|
|
29477
|
-
height = _ref.height
|
|
29569
|
+
height = _ref.height,
|
|
29570
|
+
emojisPerRow = _ref.emojisPerRow;
|
|
29478
29571
|
var category = categoryFromCategoryConfig(categoryConfig);
|
|
29479
29572
|
var categoryName = categoryNameFromCategoryConfig(categoryConfig);
|
|
29480
29573
|
return React.createElement("li", {
|
|
29481
29574
|
className: shipstyles.cx(styles$7.category, hidden && commonStyles.hidden, hiddenOnSearch && commonInteractionStyles.hiddenOnSearch),
|
|
29482
29575
|
"data-name": category,
|
|
29576
|
+
role: "rowgroup",
|
|
29483
29577
|
"aria-label": categoryName
|
|
29484
29578
|
}, React.createElement("h2", {
|
|
29485
29579
|
className: shipstyles.cx(styles$7.label)
|
|
@@ -29487,7 +29581,9 @@ function EmojiCategory(_ref) {
|
|
|
29487
29581
|
className: shipstyles.cx(styles$7.categoryContent),
|
|
29488
29582
|
style: {
|
|
29489
29583
|
height: height
|
|
29490
|
-
}
|
|
29584
|
+
},
|
|
29585
|
+
role: "row",
|
|
29586
|
+
"data-emojis-per-row": emojisPerRow
|
|
29491
29587
|
}, children));
|
|
29492
29588
|
}
|
|
29493
29589
|
var styles$7 = /*#__PURE__*/stylesheet.create({
|
|
@@ -29589,47 +29685,68 @@ function EmojiList(_ref) {
|
|
|
29589
29685
|
var getEmojisByCategory = useGetEmojisByCategory();
|
|
29590
29686
|
var labelHeight = getLabelHeight(EmojiListRef.current);
|
|
29591
29687
|
var topOffset = 0;
|
|
29592
|
-
|
|
29593
|
-
|
|
29594
|
-
|
|
29595
|
-
|
|
29596
|
-
|
|
29597
|
-
|
|
29598
|
-
|
|
29599
|
-
|
|
29600
|
-
|
|
29601
|
-
|
|
29602
|
-
|
|
29603
|
-
|
|
29604
|
-
|
|
29605
|
-
|
|
29606
|
-
|
|
29607
|
-
|
|
29608
|
-
|
|
29609
|
-
|
|
29610
|
-
|
|
29611
|
-
|
|
29612
|
-
|
|
29613
|
-
|
|
29614
|
-
|
|
29615
|
-
|
|
29616
|
-
|
|
29617
|
-
|
|
29688
|
+
// role="grid" makes screen readers switch out of virtual-cursor mode so
|
|
29689
|
+
// arrow keys reach the picker's keyboard handler. Each category is one
|
|
29690
|
+
// rowgroup: visual rows shift under virtualization, so the category is
|
|
29691
|
+
// the only stable row unit. Emoji buttons intentionally keep their
|
|
29692
|
+
// native button role for activation semantics.
|
|
29693
|
+
// https://github.com/ealush/emoji-picker-react/issues/508
|
|
29694
|
+
// The list markup is kept for a backwards-compatible DOM structure;
|
|
29695
|
+
// the grid role override is intentional.
|
|
29696
|
+
return (
|
|
29697
|
+
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-to-interactive-role
|
|
29698
|
+
React.createElement("ul", {
|
|
29699
|
+
className: shipstyles.cx(styles$8.emojiList),
|
|
29700
|
+
ref: EmojiListRef,
|
|
29701
|
+
role: "grid"
|
|
29702
|
+
}, React.createElement(MeasureEmoji, null), categories.map(function (categoryConfig, index) {
|
|
29703
|
+
var category = categoryFromCategoryConfig(categoryConfig);
|
|
29704
|
+
var currentOffset = topOffset;
|
|
29705
|
+
var categoryHeight = categoryHeights[category];
|
|
29706
|
+
if (categoryHeight) {
|
|
29707
|
+
topOffset += categoryHeight + labelHeight;
|
|
29708
|
+
}
|
|
29709
|
+
return React.createElement(RenderCategory, {
|
|
29710
|
+
key: category,
|
|
29711
|
+
categoryEmojis: getEmojisByCategory(category),
|
|
29712
|
+
categoryConfig: categoryConfig,
|
|
29713
|
+
topOffset: currentOffset,
|
|
29714
|
+
isFirstCategory: index === 0,
|
|
29715
|
+
onHeightReady: function onHeightReady(height) {
|
|
29716
|
+
if (categoryHeights[category] !== height) {
|
|
29717
|
+
setCategoryHeights(function (prev) {
|
|
29718
|
+
var _extends2;
|
|
29719
|
+
return _extends({}, prev, (_extends2 = {}, _extends2[category] = height, _extends2));
|
|
29720
|
+
});
|
|
29721
|
+
}
|
|
29722
|
+
},
|
|
29723
|
+
scrollTop: scrollTop
|
|
29724
|
+
});
|
|
29725
|
+
}))
|
|
29726
|
+
);
|
|
29618
29727
|
}
|
|
29619
29728
|
function RenderCategory(_ref2) {
|
|
29620
29729
|
var categoryEmojis = _ref2.categoryEmojis,
|
|
29621
29730
|
categoryConfig = _ref2.categoryConfig,
|
|
29622
29731
|
topOffset = _ref2.topOffset,
|
|
29623
29732
|
onHeightReady = _ref2.onHeightReady,
|
|
29624
|
-
scrollTop = _ref2.scrollTop
|
|
29733
|
+
scrollTop = _ref2.scrollTop,
|
|
29734
|
+
isFirstCategory = _ref2.isFirstCategory;
|
|
29625
29735
|
var _useVisibleCategories = useVisibleCategoriesState(),
|
|
29626
29736
|
visibleCategories = _useVisibleCategories[0];
|
|
29737
|
+
// The observer is the only writer of visibleCategories. When it never
|
|
29738
|
+
// fires (zero-height container at mount, hidden modal), no category
|
|
29739
|
+
// would ever render. The first category paints optimistically to break
|
|
29740
|
+
// the deadlock; the observer takes over from there.
|
|
29741
|
+
// https://github.com/ealush/emoji-picker-react/issues/469
|
|
29742
|
+
// https://github.com/ealush/emoji-picker-react/issues/475
|
|
29743
|
+
var isCategoryVisible = isFirstCategory || visibleCategories.includes(categoryConfig.category);
|
|
29627
29744
|
var _useEmojiVirtualizati = useEmojiVirtualization({
|
|
29628
29745
|
categoryEmojis: categoryEmojis,
|
|
29629
29746
|
topOffset: topOffset,
|
|
29630
29747
|
onHeightReady: onHeightReady,
|
|
29631
29748
|
scrollTop: scrollTop,
|
|
29632
|
-
isCategoryVisible:
|
|
29749
|
+
isCategoryVisible: isCategoryVisible
|
|
29633
29750
|
}),
|
|
29634
29751
|
virtualizedCounter = _useEmojiVirtualizati.virtualizedCounter,
|
|
29635
29752
|
emojis = _useEmojiVirtualizati.emojis,
|
|
@@ -29637,6 +29754,7 @@ function RenderCategory(_ref2) {
|
|
|
29637
29754
|
return React.createElement(EmojiCategory, {
|
|
29638
29755
|
categoryConfig: categoryConfig,
|
|
29639
29756
|
height: dimensions == null ? void 0 : dimensions.categoryHeight,
|
|
29757
|
+
emojisPerRow: dimensions == null ? void 0 : dimensions.emojisPerRow,
|
|
29640
29758
|
// Indicates that there are no visible emojis
|
|
29641
29759
|
// Hence, the category should be hidden
|
|
29642
29760
|
hidden: !emojis.length && virtualizedCounter === 0
|
|
@@ -30003,10 +30121,12 @@ function BtnSkinToneVariation(_ref) {
|
|
|
30003
30121
|
onClick = _ref.onClick,
|
|
30004
30122
|
isActive = _ref.isActive,
|
|
30005
30123
|
skinToneVariation = _ref.skinToneVariation,
|
|
30006
|
-
style = _ref.style
|
|
30124
|
+
style = _ref.style,
|
|
30125
|
+
tabIndex = _ref.tabIndex;
|
|
30007
30126
|
return React.createElement(Button, {
|
|
30008
30127
|
style: style,
|
|
30009
30128
|
onClick: onClick,
|
|
30129
|
+
tabIndex: tabIndex,
|
|
30010
30130
|
className: shipstyles.cx("epr-tone-" + skinToneVariation, styles$c.tone, !isOpen && styles$c.closedTone, isActive && styles$c.active),
|
|
30011
30131
|
"aria-pressed": isActive,
|
|
30012
30132
|
"aria-label": "Skin tone " + skinTonesNamed[skinToneVariation]
|
|
@@ -30115,6 +30235,12 @@ function SkinTonePicker(_ref) {
|
|
|
30115
30235
|
key: skinToneVariation,
|
|
30116
30236
|
skinToneVariation: skinToneVariation,
|
|
30117
30237
|
isOpen: isOpen,
|
|
30238
|
+
// Roving tabindex: when the fan is closed the inactive tones
|
|
30239
|
+
// are invisible (opacity: 0, stacked), so they must not be
|
|
30240
|
+
// reachable via Tab. The active tone stays tabbable to open
|
|
30241
|
+
// the fan from the keyboard.
|
|
30242
|
+
// https://github.com/ealush/emoji-picker-react/issues/492
|
|
30243
|
+
tabIndex: isOpen || active ? 0 : -1,
|
|
30118
30244
|
style: {
|
|
30119
30245
|
transform: shipstyles.cx(vertical ? "translateY(-" + i * (isOpen ? ITEM_SIZE : 0) + "px)" : "translateX(-" + i * (isOpen ? ITEM_SIZE : 0) + "px)", isOpen && active && 'scale(1.3)')
|
|
30120
30246
|
},
|