@hokkiai/discord-emoji-selector 1.1.5 → 1.1.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.
Files changed (3) hide show
  1. package/dist/index.cjs +131 -67
  2. package/dist/index.js +133 -69
  3. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -89,84 +89,86 @@ function useSearchValue({ pickerId }) {
89
89
 
90
90
  // src/categoryDisplay.tsx
91
91
  var import_jsx_runtime = require("react/jsx-runtime");
92
- function Emoji({
92
+ var Emoji = (0, import_react2.memo)(function Emoji2({
93
93
  pickerId,
94
94
  emoji,
95
95
  onEmojiMouseEnter,
96
96
  onEmojiMouseLeave,
97
- onEmojiSelect,
98
- key
97
+ onEmojiSelect
99
98
  }) {
99
+ const handleMouseEnter = (0, import_react2.useCallback)(() => {
100
+ window["emojipicker-" + pickerId].changeFooterEmoji(emoji);
101
+ window["emojipicker-" + pickerId].changeSearchbarPlaceholder(emoji.name);
102
+ onEmojiMouseEnter(emoji);
103
+ }, [pickerId, emoji, onEmojiMouseEnter]);
104
+ const handleMouseLeave = (0, import_react2.useCallback)(() => {
105
+ onEmojiMouseLeave(emoji);
106
+ }, [emoji, onEmojiMouseLeave]);
107
+ const handleClick = (0, import_react2.useCallback)(() => {
108
+ onEmojiSelect(emoji);
109
+ }, [emoji, onEmojiSelect]);
110
+ const html = (0, import_react2.useMemo)(() => render(emoji.char), [emoji.char]);
100
111
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
101
112
  "div",
102
113
  {
103
- onMouseEnter: () => {
104
- window["emojipicker-" + pickerId].changeFooterEmoji(emoji);
105
- window["emojipicker-" + pickerId].changeSearchbarPlaceholder(
106
- emoji.name
107
- );
108
- onEmojiMouseEnter(emoji);
109
- },
110
- onMouseLeave: () => {
111
- onEmojiMouseLeave(emoji);
112
- },
113
- onClick: () => {
114
- onEmojiSelect(emoji);
115
- },
116
- className: "HOKKIEMOJIPICKER-emoji text-4xl p-1 cursor-pointer hover:bg-white/15 rounded-sm size-12.5 flex items-center justify-center overflow-hidden",
117
- dangerouslySetInnerHTML: {
118
- __html: render(emoji.char)
119
- }
114
+ tabIndex: -1,
115
+ onMouseEnter: handleMouseEnter,
116
+ onMouseLeave: handleMouseLeave,
117
+ onClick: handleClick,
118
+ className: "HOKKIEMOJIPICKER-emoji text-4xl p-1 cursor-pointer hover:bg-white/15 focus:bg-white/20 rounded-sm size-12.5 flex items-center justify-center overflow-hidden",
119
+ dangerouslySetInnerHTML: { __html: html }
120
120
  }
121
121
  );
122
- }
123
- function SkinEmoji({
122
+ });
123
+ var SkinEmoji = (0, import_react2.memo)(function SkinEmoji2({
124
124
  pickerId,
125
125
  emoji,
126
126
  onEmojiSelect,
127
127
  onEmojiMouseEnter,
128
- onEmojiMouseLeave,
129
- key
128
+ onEmojiMouseLeave
130
129
  }) {
131
130
  const skin = useSkin({ pickerId });
132
- const grabEmoji = () => [1, 2, 3, 4, 5].includes(skin) ? emoji.tones.find((a) => a.tone.find((b) => b === skin) === skin)?.char : emoji.char;
133
- const fakeEmoji = {
134
- ...emoji,
135
- char: grabEmoji(),
136
- preRendered: true,
137
- tones: [
138
- {
139
- name: emoji.tones[0].name.replaceAll("1", "0"),
140
- tone: [0],
141
- char: emoji.char
142
- },
143
- ...emoji.tones
144
- ]
145
- };
131
+ const fakeEmoji = (0, import_react2.useMemo)(() => {
132
+ const charForSkin = [1, 2, 3, 4, 5].includes(skin) ? emoji.tones.find((a) => a.tone.find((b) => b === skin) === skin)?.char : emoji.char;
133
+ return {
134
+ ...emoji,
135
+ char: charForSkin,
136
+ preRendered: true,
137
+ tones: [
138
+ {
139
+ name: emoji.tones[0].name.replaceAll("1", "0"),
140
+ tone: [0],
141
+ char: emoji.char
142
+ },
143
+ ...emoji.tones
144
+ ]
145
+ };
146
+ }, [emoji, skin]);
147
+ const handleMouseEnter = (0, import_react2.useCallback)(() => {
148
+ window["emojipicker-" + pickerId].changeFooterEmoji(fakeEmoji);
149
+ window["emojipicker-" + pickerId].changeSearchbarPlaceholder(emoji.name);
150
+ onEmojiMouseEnter(fakeEmoji);
151
+ }, [pickerId, fakeEmoji, emoji.name, onEmojiMouseEnter]);
152
+ const handleMouseLeave = (0, import_react2.useCallback)(() => {
153
+ onEmojiMouseLeave(fakeEmoji);
154
+ }, [fakeEmoji, onEmojiMouseLeave]);
155
+ const handleClick = (0, import_react2.useCallback)(() => {
156
+ onEmojiSelect(fakeEmoji);
157
+ }, [fakeEmoji, onEmojiSelect]);
158
+ const html = (0, import_react2.useMemo)(() => render(fakeEmoji.char), [fakeEmoji.char]);
146
159
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
147
160
  "div",
148
161
  {
149
- onMouseEnter: () => {
150
- window["emojipicker-" + pickerId].changeFooterEmoji(fakeEmoji);
151
- window["emojipicker-" + pickerId].changeSearchbarPlaceholder(
152
- emoji.name
153
- );
154
- onEmojiMouseEnter(fakeEmoji);
155
- },
156
- onMouseLeave: () => {
157
- onEmojiMouseLeave(fakeEmoji);
158
- },
159
- onClick: () => {
160
- onEmojiSelect(fakeEmoji);
161
- },
162
- className: "HOKKIEMOJIPICKER-skinemoji text-4xl p-1 cursor-pointer hover:bg-white/15 rounded-sm size-12.5 flex items-center justify-center overflow-hidden",
163
- dangerouslySetInnerHTML: {
164
- __html: render(fakeEmoji.char)
165
- }
162
+ tabIndex: -1,
163
+ onMouseEnter: handleMouseEnter,
164
+ onMouseLeave: handleMouseLeave,
165
+ onClick: handleClick,
166
+ className: "HOKKIEMOJIPICKER-skinemoji text-4xl p-1 cursor-pointer hover:bg-white/15 focus:bg-white/20 rounded-sm size-12.5 flex items-center justify-center overflow-hidden",
167
+ dangerouslySetInnerHTML: { __html: html }
166
168
  }
167
169
  );
168
- }
169
- function CategoryDisplay({
170
+ });
171
+ var CategoryDisplay = (0, import_react2.memo)(function CategoryDisplay2({
170
172
  category,
171
173
  categoryInfo,
172
174
  isToneSelectorEnabled,
@@ -183,13 +185,21 @@ function CategoryDisplay({
183
185
  "hokkiemojipicker-category-" + category.name + "-open"
184
186
  ) || "true") === "true"
185
187
  );
186
- }, []);
187
- if ((searchValue || "").length > 0) {
188
+ }, [category.name]);
189
+ const searchLower = (0, import_react2.useMemo)(
190
+ () => (searchValue || "").toLowerCase().replace(/_/g, " "),
191
+ [searchValue]
192
+ );
193
+ const filteredEmojis = (0, import_react2.useMemo)(() => {
194
+ if (!searchLower) return null;
195
+ return category.emojis.filter(
196
+ (emoji) => emoji.name.toLowerCase().replace(/_/g, " ").includes(searchLower)
197
+ );
198
+ }, [category.emojis, searchLower]);
199
+ if (searchLower) {
188
200
  if (category.name === "recentlyUsed")
189
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-1.5 w-full " });
190
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: category.emojis.map((emoji) => {
191
- if (!emoji.name.toLowerCase().replace(/_/g, " ").includes(searchValue.toLowerCase().replace(/_/g, " ")))
192
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, {});
201
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "h-1.5 w-full" });
202
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: filteredEmojis.map((emoji) => {
193
203
  if (emoji.hasTone && !emoji.preRendered) {
194
204
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
195
205
  SkinEmoji,
@@ -271,7 +281,8 @@ function CategoryDisplay({
271
281
  );
272
282
  }) })
273
283
  ] });
274
- }
284
+ });
285
+ var categoryDisplay_default = CategoryDisplay;
275
286
 
276
287
  // src/emojilib.json
277
288
  var emojilib_default = [
@@ -17174,7 +17185,10 @@ function SidebarCategory({
17174
17185
  const categoryRect = categoryContainer.getBoundingClientRect();
17175
17186
  const headerHeight = header.offsetHeight;
17176
17187
  const targetTop = categoryRect.top - containerRect.top + scrollElem.scrollTop - headerHeight;
17177
- scrollElem.scrollTo({ top: Math.max(0, targetTop), behavior: "smooth" });
17188
+ scrollElem.scrollTo({
17189
+ top: Math.max(0, targetTop),
17190
+ behavior: "smooth"
17191
+ });
17178
17192
  };
17179
17193
  if ((searchValue || "").length > 0) {
17180
17194
  window["emojipicker-" + id].setSearchValue("");
@@ -17527,8 +17541,55 @@ function EmojiSelector({
17527
17541
  ...emojilib_default
17528
17542
  ].filter((a) => a !== void 0);
17529
17543
  const navHeight = showNav ? Math.floor(height / 7) : 0;
17530
- const id = Math.random().toString(36).substring(2, 15);
17544
+ const id = (0, import_react7.useMemo)(
17545
+ () => Math.random().toString(36).substring(2, 15),
17546
+ []
17547
+ );
17531
17548
  const picker = (0, import_react7.useRef)(null);
17549
+ const scrollRef = (0, import_react7.useRef)(null);
17550
+ const [focusIndex, setFocusIndex] = (0, import_react7.useState)(-1);
17551
+ const handleWheel = (0, import_react7.useCallback)((e) => {
17552
+ const el = scrollRef.current;
17553
+ if (!el) return;
17554
+ e.preventDefault();
17555
+ el.scrollTop += e.deltaY;
17556
+ }, []);
17557
+ (0, import_react7.useEffect)(() => {
17558
+ const el = scrollRef.current;
17559
+ if (!el) return;
17560
+ el.addEventListener("wheel", handleWheel, { passive: false });
17561
+ return () => {
17562
+ el.removeEventListener("wheel", handleWheel);
17563
+ };
17564
+ }, [handleWheel]);
17565
+ const handleKeyDown = (0, import_react7.useCallback)(
17566
+ (e) => {
17567
+ const el = scrollRef.current;
17568
+ if (!el) return;
17569
+ const emojiElements = el.querySelectorAll(
17570
+ ".HOKKIEMOJIPICKER-emoji, .HOKKIEMOJIPICKER-skinemoji"
17571
+ );
17572
+ const count = emojiElements.length;
17573
+ if (count === 0) return;
17574
+ if (e.key === "Tab") {
17575
+ e.preventDefault();
17576
+ let next = focusIndex + (e.shiftKey ? -1 : 1);
17577
+ if (next < 0) next = count - 1;
17578
+ if (next >= count) next = 0;
17579
+ setFocusIndex(next);
17580
+ const target = emojiElements[next];
17581
+ if (target) {
17582
+ target.scrollIntoView({ block: "nearest" });
17583
+ target.focus();
17584
+ }
17585
+ } else if (e.key === "Enter" && focusIndex >= 0) {
17586
+ e.preventDefault();
17587
+ const target = emojiElements[focusIndex];
17588
+ if (target) target.click();
17589
+ }
17590
+ },
17591
+ [focusIndex]
17592
+ );
17532
17593
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "HOKKIEMOJIPICKER-emojiContainer", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
17533
17594
  "div",
17534
17595
  {
@@ -17596,6 +17657,9 @@ function EmojiSelector({
17596
17657
  /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
17597
17658
  "div",
17598
17659
  {
17660
+ ref: scrollRef,
17661
+ tabIndex: 0,
17662
+ onKeyDown: handleKeyDown,
17599
17663
  style: {
17600
17664
  flexBasis: "fit-content"
17601
17665
  },
@@ -17604,7 +17668,7 @@ function EmojiSelector({
17604
17668
  if (!category) return;
17605
17669
  const categoryInfo = categoryData[category.name];
17606
17670
  return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
17607
- CategoryDisplay,
17671
+ categoryDisplay_default,
17608
17672
  {
17609
17673
  onEmojiMouseEnter,
17610
17674
  onEmojiMouseLeave,
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  // src/index.tsx
2
- import { useRef as useRef2 } from "react";
2
+ import { useCallback as useCallback2, useEffect as useEffect7, useMemo as useMemo2, useRef as useRef2, useState as useState7 } from "react";
3
3
 
4
4
  // src/categoryDisplay.tsx
5
5
  import { ChevronDown } from "lucide-react";
6
- import { useEffect as useEffect2, useState as useState2 } from "react";
6
+ import { memo, useEffect as useEffect2, useState as useState2, useCallback, useMemo } from "react";
7
7
 
8
8
  // src/render.tsx
9
9
  import tw from "@twemoji/api";
@@ -55,84 +55,86 @@ function useSearchValue({ pickerId }) {
55
55
 
56
56
  // src/categoryDisplay.tsx
57
57
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
58
- function Emoji({
58
+ var Emoji = memo(function Emoji2({
59
59
  pickerId,
60
60
  emoji,
61
61
  onEmojiMouseEnter,
62
62
  onEmojiMouseLeave,
63
- onEmojiSelect,
64
- key
63
+ onEmojiSelect
65
64
  }) {
65
+ const handleMouseEnter = useCallback(() => {
66
+ window["emojipicker-" + pickerId].changeFooterEmoji(emoji);
67
+ window["emojipicker-" + pickerId].changeSearchbarPlaceholder(emoji.name);
68
+ onEmojiMouseEnter(emoji);
69
+ }, [pickerId, emoji, onEmojiMouseEnter]);
70
+ const handleMouseLeave = useCallback(() => {
71
+ onEmojiMouseLeave(emoji);
72
+ }, [emoji, onEmojiMouseLeave]);
73
+ const handleClick = useCallback(() => {
74
+ onEmojiSelect(emoji);
75
+ }, [emoji, onEmojiSelect]);
76
+ const html = useMemo(() => render(emoji.char), [emoji.char]);
66
77
  return /* @__PURE__ */ jsx(
67
78
  "div",
68
79
  {
69
- onMouseEnter: () => {
70
- window["emojipicker-" + pickerId].changeFooterEmoji(emoji);
71
- window["emojipicker-" + pickerId].changeSearchbarPlaceholder(
72
- emoji.name
73
- );
74
- onEmojiMouseEnter(emoji);
75
- },
76
- onMouseLeave: () => {
77
- onEmojiMouseLeave(emoji);
78
- },
79
- onClick: () => {
80
- onEmojiSelect(emoji);
81
- },
82
- className: "HOKKIEMOJIPICKER-emoji text-4xl p-1 cursor-pointer hover:bg-white/15 rounded-sm size-12.5 flex items-center justify-center overflow-hidden",
83
- dangerouslySetInnerHTML: {
84
- __html: render(emoji.char)
85
- }
80
+ tabIndex: -1,
81
+ onMouseEnter: handleMouseEnter,
82
+ onMouseLeave: handleMouseLeave,
83
+ onClick: handleClick,
84
+ className: "HOKKIEMOJIPICKER-emoji text-4xl p-1 cursor-pointer hover:bg-white/15 focus:bg-white/20 rounded-sm size-12.5 flex items-center justify-center overflow-hidden",
85
+ dangerouslySetInnerHTML: { __html: html }
86
86
  }
87
87
  );
88
- }
89
- function SkinEmoji({
88
+ });
89
+ var SkinEmoji = memo(function SkinEmoji2({
90
90
  pickerId,
91
91
  emoji,
92
92
  onEmojiSelect,
93
93
  onEmojiMouseEnter,
94
- onEmojiMouseLeave,
95
- key
94
+ onEmojiMouseLeave
96
95
  }) {
97
96
  const skin = useSkin({ pickerId });
98
- const grabEmoji = () => [1, 2, 3, 4, 5].includes(skin) ? emoji.tones.find((a) => a.tone.find((b) => b === skin) === skin)?.char : emoji.char;
99
- const fakeEmoji = {
100
- ...emoji,
101
- char: grabEmoji(),
102
- preRendered: true,
103
- tones: [
104
- {
105
- name: emoji.tones[0].name.replaceAll("1", "0"),
106
- tone: [0],
107
- char: emoji.char
108
- },
109
- ...emoji.tones
110
- ]
111
- };
97
+ const fakeEmoji = useMemo(() => {
98
+ const charForSkin = [1, 2, 3, 4, 5].includes(skin) ? emoji.tones.find((a) => a.tone.find((b) => b === skin) === skin)?.char : emoji.char;
99
+ return {
100
+ ...emoji,
101
+ char: charForSkin,
102
+ preRendered: true,
103
+ tones: [
104
+ {
105
+ name: emoji.tones[0].name.replaceAll("1", "0"),
106
+ tone: [0],
107
+ char: emoji.char
108
+ },
109
+ ...emoji.tones
110
+ ]
111
+ };
112
+ }, [emoji, skin]);
113
+ const handleMouseEnter = useCallback(() => {
114
+ window["emojipicker-" + pickerId].changeFooterEmoji(fakeEmoji);
115
+ window["emojipicker-" + pickerId].changeSearchbarPlaceholder(emoji.name);
116
+ onEmojiMouseEnter(fakeEmoji);
117
+ }, [pickerId, fakeEmoji, emoji.name, onEmojiMouseEnter]);
118
+ const handleMouseLeave = useCallback(() => {
119
+ onEmojiMouseLeave(fakeEmoji);
120
+ }, [fakeEmoji, onEmojiMouseLeave]);
121
+ const handleClick = useCallback(() => {
122
+ onEmojiSelect(fakeEmoji);
123
+ }, [fakeEmoji, onEmojiSelect]);
124
+ const html = useMemo(() => render(fakeEmoji.char), [fakeEmoji.char]);
112
125
  return /* @__PURE__ */ jsx(
113
126
  "div",
114
127
  {
115
- onMouseEnter: () => {
116
- window["emojipicker-" + pickerId].changeFooterEmoji(fakeEmoji);
117
- window["emojipicker-" + pickerId].changeSearchbarPlaceholder(
118
- emoji.name
119
- );
120
- onEmojiMouseEnter(fakeEmoji);
121
- },
122
- onMouseLeave: () => {
123
- onEmojiMouseLeave(fakeEmoji);
124
- },
125
- onClick: () => {
126
- onEmojiSelect(fakeEmoji);
127
- },
128
- className: "HOKKIEMOJIPICKER-skinemoji text-4xl p-1 cursor-pointer hover:bg-white/15 rounded-sm size-12.5 flex items-center justify-center overflow-hidden",
129
- dangerouslySetInnerHTML: {
130
- __html: render(fakeEmoji.char)
131
- }
128
+ tabIndex: -1,
129
+ onMouseEnter: handleMouseEnter,
130
+ onMouseLeave: handleMouseLeave,
131
+ onClick: handleClick,
132
+ className: "HOKKIEMOJIPICKER-skinemoji text-4xl p-1 cursor-pointer hover:bg-white/15 focus:bg-white/20 rounded-sm size-12.5 flex items-center justify-center overflow-hidden",
133
+ dangerouslySetInnerHTML: { __html: html }
132
134
  }
133
135
  );
134
- }
135
- function CategoryDisplay({
136
+ });
137
+ var CategoryDisplay = memo(function CategoryDisplay2({
136
138
  category,
137
139
  categoryInfo,
138
140
  isToneSelectorEnabled,
@@ -149,13 +151,21 @@ function CategoryDisplay({
149
151
  "hokkiemojipicker-category-" + category.name + "-open"
150
152
  ) || "true") === "true"
151
153
  );
152
- }, []);
153
- if ((searchValue || "").length > 0) {
154
+ }, [category.name]);
155
+ const searchLower = useMemo(
156
+ () => (searchValue || "").toLowerCase().replace(/_/g, " "),
157
+ [searchValue]
158
+ );
159
+ const filteredEmojis = useMemo(() => {
160
+ if (!searchLower) return null;
161
+ return category.emojis.filter(
162
+ (emoji) => emoji.name.toLowerCase().replace(/_/g, " ").includes(searchLower)
163
+ );
164
+ }, [category.emojis, searchLower]);
165
+ if (searchLower) {
154
166
  if (category.name === "recentlyUsed")
155
- return /* @__PURE__ */ jsx("div", { className: "h-1.5 w-full " });
156
- return /* @__PURE__ */ jsx(Fragment, { children: category.emojis.map((emoji) => {
157
- if (!emoji.name.toLowerCase().replace(/_/g, " ").includes(searchValue.toLowerCase().replace(/_/g, " ")))
158
- return /* @__PURE__ */ jsx(Fragment, {});
167
+ return /* @__PURE__ */ jsx("div", { className: "h-1.5 w-full" });
168
+ return /* @__PURE__ */ jsx(Fragment, { children: filteredEmojis.map((emoji) => {
159
169
  if (emoji.hasTone && !emoji.preRendered) {
160
170
  return /* @__PURE__ */ jsx(
161
171
  SkinEmoji,
@@ -237,7 +247,8 @@ function CategoryDisplay({
237
247
  );
238
248
  }) })
239
249
  ] });
240
- }
250
+ });
251
+ var categoryDisplay_default = CategoryDisplay;
241
252
 
242
253
  // src/emojilib.json
243
254
  var emojilib_default = [
@@ -17140,7 +17151,10 @@ function SidebarCategory({
17140
17151
  const categoryRect = categoryContainer.getBoundingClientRect();
17141
17152
  const headerHeight = header.offsetHeight;
17142
17153
  const targetTop = categoryRect.top - containerRect.top + scrollElem.scrollTop - headerHeight;
17143
- scrollElem.scrollTo({ top: Math.max(0, targetTop), behavior: "smooth" });
17154
+ scrollElem.scrollTo({
17155
+ top: Math.max(0, targetTop),
17156
+ behavior: "smooth"
17157
+ });
17144
17158
  };
17145
17159
  if ((searchValue || "").length > 0) {
17146
17160
  window["emojipicker-" + id].setSearchValue("");
@@ -17493,8 +17507,55 @@ function EmojiSelector({
17493
17507
  ...emojilib_default
17494
17508
  ].filter((a) => a !== void 0);
17495
17509
  const navHeight = showNav ? Math.floor(height / 7) : 0;
17496
- const id = Math.random().toString(36).substring(2, 15);
17510
+ const id = useMemo2(
17511
+ () => Math.random().toString(36).substring(2, 15),
17512
+ []
17513
+ );
17497
17514
  const picker = useRef2(null);
17515
+ const scrollRef = useRef2(null);
17516
+ const [focusIndex, setFocusIndex] = useState7(-1);
17517
+ const handleWheel = useCallback2((e) => {
17518
+ const el = scrollRef.current;
17519
+ if (!el) return;
17520
+ e.preventDefault();
17521
+ el.scrollTop += e.deltaY;
17522
+ }, []);
17523
+ useEffect7(() => {
17524
+ const el = scrollRef.current;
17525
+ if (!el) return;
17526
+ el.addEventListener("wheel", handleWheel, { passive: false });
17527
+ return () => {
17528
+ el.removeEventListener("wheel", handleWheel);
17529
+ };
17530
+ }, [handleWheel]);
17531
+ const handleKeyDown = useCallback2(
17532
+ (e) => {
17533
+ const el = scrollRef.current;
17534
+ if (!el) return;
17535
+ const emojiElements = el.querySelectorAll(
17536
+ ".HOKKIEMOJIPICKER-emoji, .HOKKIEMOJIPICKER-skinemoji"
17537
+ );
17538
+ const count = emojiElements.length;
17539
+ if (count === 0) return;
17540
+ if (e.key === "Tab") {
17541
+ e.preventDefault();
17542
+ let next = focusIndex + (e.shiftKey ? -1 : 1);
17543
+ if (next < 0) next = count - 1;
17544
+ if (next >= count) next = 0;
17545
+ setFocusIndex(next);
17546
+ const target = emojiElements[next];
17547
+ if (target) {
17548
+ target.scrollIntoView({ block: "nearest" });
17549
+ target.focus();
17550
+ }
17551
+ } else if (e.key === "Enter" && focusIndex >= 0) {
17552
+ e.preventDefault();
17553
+ const target = emojiElements[focusIndex];
17554
+ if (target) target.click();
17555
+ }
17556
+ },
17557
+ [focusIndex]
17558
+ );
17498
17559
  return /* @__PURE__ */ jsx7("div", { className: "HOKKIEMOJIPICKER-emojiContainer", children: /* @__PURE__ */ jsxs6(
17499
17560
  "div",
17500
17561
  {
@@ -17562,6 +17623,9 @@ function EmojiSelector({
17562
17623
  /* @__PURE__ */ jsx7(
17563
17624
  "div",
17564
17625
  {
17626
+ ref: scrollRef,
17627
+ tabIndex: 0,
17628
+ onKeyDown: handleKeyDown,
17565
17629
  style: {
17566
17630
  flexBasis: "fit-content"
17567
17631
  },
@@ -17570,7 +17634,7 @@ function EmojiSelector({
17570
17634
  if (!category) return;
17571
17635
  const categoryInfo = categoryData[category.name];
17572
17636
  return /* @__PURE__ */ jsx7(
17573
- CategoryDisplay,
17637
+ categoryDisplay_default,
17574
17638
  {
17575
17639
  onEmojiMouseEnter,
17576
17640
  onEmojiMouseLeave,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hokkiai/discord-emoji-selector",
3
- "version": "1.1.5",
3
+ "version": "1.1.6",
4
4
  "description": "A lightweight & powerful discord-style emoji picker",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",