@hokkiai/discord-emoji-selector 1.1.4 → 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.
- package/dist/index.cjs +178 -108
- package/dist/index.js +180 -110
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -57,116 +57,118 @@ var import_react = require("react");
|
|
|
57
57
|
function useSkin({ pickerId }) {
|
|
58
58
|
const [skin, setSkin] = (0, import_react.useState)(0);
|
|
59
59
|
(0, import_react.useEffect)(() => {
|
|
60
|
-
|
|
60
|
+
let raf;
|
|
61
|
+
const loop = () => {
|
|
61
62
|
const newSkin = window["emojipicker-" + pickerId]?.skin;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}, 100);
|
|
63
|
+
setSkin(newSkin || 0);
|
|
64
|
+
raf = requestAnimationFrame(loop);
|
|
65
|
+
};
|
|
66
|
+
raf = requestAnimationFrame(loop);
|
|
67
67
|
return () => {
|
|
68
|
-
|
|
68
|
+
cancelAnimationFrame(raf);
|
|
69
69
|
};
|
|
70
|
-
}, [
|
|
70
|
+
}, [pickerId]);
|
|
71
71
|
return skin;
|
|
72
72
|
}
|
|
73
73
|
function useSearchValue({ pickerId }) {
|
|
74
74
|
const [searchValue, setSearchValue] = (0, import_react.useState)("");
|
|
75
75
|
(0, import_react.useEffect)(() => {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
76
|
+
let raf;
|
|
77
|
+
const loop = () => {
|
|
78
|
+
const newSearchValue = window["emojipicker-" + pickerId]?.searchValue || "";
|
|
79
|
+
setSearchValue(newSearchValue);
|
|
80
|
+
raf = requestAnimationFrame(loop);
|
|
81
|
+
};
|
|
82
|
+
raf = requestAnimationFrame(loop);
|
|
83
83
|
return () => {
|
|
84
|
-
|
|
84
|
+
cancelAnimationFrame(raf);
|
|
85
85
|
};
|
|
86
|
-
}, [
|
|
86
|
+
}, [pickerId]);
|
|
87
87
|
return searchValue;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
// src/categoryDisplay.tsx
|
|
91
91
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
92
|
-
|
|
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
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
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
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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 = [
|
|
@@ -17122,7 +17133,6 @@ function SidebarCategory({
|
|
|
17122
17133
|
}) {
|
|
17123
17134
|
const searchValue = useSearchValue({ pickerId: id });
|
|
17124
17135
|
const [isActive, setIsActive] = (0, import_react5.useState)(false);
|
|
17125
|
-
const [scrollTop, setScrollTop] = (0, import_react5.useState)(0);
|
|
17126
17136
|
(0, import_react5.useEffect)(() => {
|
|
17127
17137
|
if ((searchValue || "").length > 0) {
|
|
17128
17138
|
setIsActive(false);
|
|
@@ -17136,16 +17146,18 @@ function SidebarCategory({
|
|
|
17136
17146
|
);
|
|
17137
17147
|
if (!header) return;
|
|
17138
17148
|
const categoryContainer = header.parentElement;
|
|
17139
|
-
const
|
|
17140
|
-
const scrollEnd = categoryContainer.offsetTop + categoryContainer.offsetHeight - header.offsetHeight * 5.5;
|
|
17141
|
-
const actualScroll = elem.querySelector(
|
|
17149
|
+
const scrollElem = elem.querySelector(
|
|
17142
17150
|
".HOKKIEMOJIPICKER-emojidisplay"
|
|
17143
|
-
)
|
|
17144
|
-
if (
|
|
17145
|
-
|
|
17146
|
-
|
|
17147
|
-
|
|
17148
|
-
|
|
17151
|
+
);
|
|
17152
|
+
if (!scrollElem) return;
|
|
17153
|
+
const containerRect = scrollElem.getBoundingClientRect();
|
|
17154
|
+
const categoryRect = categoryContainer.getBoundingClientRect();
|
|
17155
|
+
const headerHeight = header.offsetHeight;
|
|
17156
|
+
const categoryTop = categoryRect.top - containerRect.top + scrollElem.scrollTop;
|
|
17157
|
+
const categoryBottom = categoryTop + categoryContainer.offsetHeight;
|
|
17158
|
+
const sTop = scrollElem.scrollTop;
|
|
17159
|
+
const inView = sTop >= categoryTop - headerHeight && sTop < categoryBottom - headerHeight;
|
|
17160
|
+
setIsActive(inView);
|
|
17149
17161
|
};
|
|
17150
17162
|
elem.querySelector(".HOKKIEMOJIPICKER-emojidisplay").addEventListener("scroll", updateActive);
|
|
17151
17163
|
updateActive();
|
|
@@ -17153,28 +17165,37 @@ function SidebarCategory({
|
|
|
17153
17165
|
elem.querySelector(".HOKKIEMOJIPICKER-emojidisplay").removeEventListener("scroll", updateActive);
|
|
17154
17166
|
};
|
|
17155
17167
|
}, [picker, searchValue]);
|
|
17156
|
-
(0, import_react5.useEffect)(() => {
|
|
17157
|
-
const elem = picker.current;
|
|
17158
|
-
if (!elem) return;
|
|
17159
|
-
const header = elem.querySelector(
|
|
17160
|
-
".HOKKIEMOJIPICKER-categoryHeader." + categoryName
|
|
17161
|
-
);
|
|
17162
|
-
const categoryContainer = header.parentElement;
|
|
17163
|
-
const scrollTop2 = categoryContainer.offsetTop - header.offsetHeight * 5.5;
|
|
17164
|
-
setScrollTop(scrollTop2);
|
|
17165
|
-
}, []);
|
|
17166
17168
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
17167
17169
|
"button",
|
|
17168
17170
|
{
|
|
17169
17171
|
onClick: () => {
|
|
17170
17172
|
const elem = picker.current;
|
|
17173
|
+
if (!elem) return;
|
|
17174
|
+
const doScroll = () => {
|
|
17175
|
+
const header = elem.querySelector(
|
|
17176
|
+
".HOKKIEMOJIPICKER-categoryHeader." + categoryName
|
|
17177
|
+
);
|
|
17178
|
+
if (!header) return;
|
|
17179
|
+
const categoryContainer = header.parentElement;
|
|
17180
|
+
const scrollElem = elem.querySelector(
|
|
17181
|
+
".HOKKIEMOJIPICKER-emojidisplay"
|
|
17182
|
+
);
|
|
17183
|
+
if (!scrollElem) return;
|
|
17184
|
+
const containerRect = scrollElem.getBoundingClientRect();
|
|
17185
|
+
const categoryRect = categoryContainer.getBoundingClientRect();
|
|
17186
|
+
const headerHeight = header.offsetHeight;
|
|
17187
|
+
const targetTop = categoryRect.top - containerRect.top + scrollElem.scrollTop - headerHeight;
|
|
17188
|
+
scrollElem.scrollTo({
|
|
17189
|
+
top: Math.max(0, targetTop),
|
|
17190
|
+
behavior: "smooth"
|
|
17191
|
+
});
|
|
17192
|
+
};
|
|
17171
17193
|
if ((searchValue || "").length > 0) {
|
|
17172
17194
|
window["emojipicker-" + id].setSearchValue("");
|
|
17173
|
-
setTimeout(
|
|
17174
|
-
|
|
17175
|
-
|
|
17195
|
+
setTimeout(doScroll, 50);
|
|
17196
|
+
} else {
|
|
17197
|
+
doScroll();
|
|
17176
17198
|
}
|
|
17177
|
-
elem.querySelector(".HOKKIEMOJIPICKER-emojidisplay").scrollTop = scrollTop;
|
|
17178
17199
|
},
|
|
17179
17200
|
"data-active": isActive ? "true" : "false",
|
|
17180
17201
|
className: "!outline-0 HOKKIEMOJIPICKER-sidebarButton cursor-pointer size-8 data-[active=true]:bg-white/5 hover:bg-white/10 overflow-hidden *:!size-6.5 transition-all hover:*:!opacity-85 data-[active=true]:*:!opacity-100 flex items-center justify-center rounded-sm *:opacity-50",
|
|
@@ -17520,9 +17541,55 @@ function EmojiSelector({
|
|
|
17520
17541
|
...emojilib_default
|
|
17521
17542
|
].filter((a) => a !== void 0);
|
|
17522
17543
|
const navHeight = showNav ? Math.floor(height / 7) : 0;
|
|
17523
|
-
const id =
|
|
17544
|
+
const id = (0, import_react7.useMemo)(
|
|
17545
|
+
() => Math.random().toString(36).substring(2, 15),
|
|
17546
|
+
[]
|
|
17547
|
+
);
|
|
17524
17548
|
const picker = (0, import_react7.useRef)(null);
|
|
17525
|
-
|
|
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
|
+
);
|
|
17526
17593
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "HOKKIEMOJIPICKER-emojiContainer", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
17527
17594
|
"div",
|
|
17528
17595
|
{
|
|
@@ -17590,6 +17657,9 @@ function EmojiSelector({
|
|
|
17590
17657
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
17591
17658
|
"div",
|
|
17592
17659
|
{
|
|
17660
|
+
ref: scrollRef,
|
|
17661
|
+
tabIndex: 0,
|
|
17662
|
+
onKeyDown: handleKeyDown,
|
|
17593
17663
|
style: {
|
|
17594
17664
|
flexBasis: "fit-content"
|
|
17595
17665
|
},
|
|
@@ -17598,7 +17668,7 @@ function EmojiSelector({
|
|
|
17598
17668
|
if (!category) return;
|
|
17599
17669
|
const categoryInfo = categoryData[category.name];
|
|
17600
17670
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
17601
|
-
|
|
17671
|
+
categoryDisplay_default,
|
|
17602
17672
|
{
|
|
17603
17673
|
onEmojiMouseEnter,
|
|
17604
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";
|
|
@@ -23,116 +23,118 @@ import { useEffect, useState } from "react";
|
|
|
23
23
|
function useSkin({ pickerId }) {
|
|
24
24
|
const [skin, setSkin] = useState(0);
|
|
25
25
|
useEffect(() => {
|
|
26
|
-
|
|
26
|
+
let raf;
|
|
27
|
+
const loop = () => {
|
|
27
28
|
const newSkin = window["emojipicker-" + pickerId]?.skin;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}, 100);
|
|
29
|
+
setSkin(newSkin || 0);
|
|
30
|
+
raf = requestAnimationFrame(loop);
|
|
31
|
+
};
|
|
32
|
+
raf = requestAnimationFrame(loop);
|
|
33
33
|
return () => {
|
|
34
|
-
|
|
34
|
+
cancelAnimationFrame(raf);
|
|
35
35
|
};
|
|
36
|
-
}, [
|
|
36
|
+
}, [pickerId]);
|
|
37
37
|
return skin;
|
|
38
38
|
}
|
|
39
39
|
function useSearchValue({ pickerId }) {
|
|
40
40
|
const [searchValue, setSearchValue] = useState("");
|
|
41
41
|
useEffect(() => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
let raf;
|
|
43
|
+
const loop = () => {
|
|
44
|
+
const newSearchValue = window["emojipicker-" + pickerId]?.searchValue || "";
|
|
45
|
+
setSearchValue(newSearchValue);
|
|
46
|
+
raf = requestAnimationFrame(loop);
|
|
47
|
+
};
|
|
48
|
+
raf = requestAnimationFrame(loop);
|
|
49
49
|
return () => {
|
|
50
|
-
|
|
50
|
+
cancelAnimationFrame(raf);
|
|
51
51
|
};
|
|
52
|
-
}, [
|
|
52
|
+
}, [pickerId]);
|
|
53
53
|
return searchValue;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
// src/categoryDisplay.tsx
|
|
57
57
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
58
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
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
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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 = [
|
|
@@ -17088,7 +17099,6 @@ function SidebarCategory({
|
|
|
17088
17099
|
}) {
|
|
17089
17100
|
const searchValue = useSearchValue({ pickerId: id });
|
|
17090
17101
|
const [isActive, setIsActive] = useState5(false);
|
|
17091
|
-
const [scrollTop, setScrollTop] = useState5(0);
|
|
17092
17102
|
useEffect5(() => {
|
|
17093
17103
|
if ((searchValue || "").length > 0) {
|
|
17094
17104
|
setIsActive(false);
|
|
@@ -17102,16 +17112,18 @@ function SidebarCategory({
|
|
|
17102
17112
|
);
|
|
17103
17113
|
if (!header) return;
|
|
17104
17114
|
const categoryContainer = header.parentElement;
|
|
17105
|
-
const
|
|
17106
|
-
const scrollEnd = categoryContainer.offsetTop + categoryContainer.offsetHeight - header.offsetHeight * 5.5;
|
|
17107
|
-
const actualScroll = elem.querySelector(
|
|
17115
|
+
const scrollElem = elem.querySelector(
|
|
17108
17116
|
".HOKKIEMOJIPICKER-emojidisplay"
|
|
17109
|
-
)
|
|
17110
|
-
if (
|
|
17111
|
-
|
|
17112
|
-
|
|
17113
|
-
|
|
17114
|
-
|
|
17117
|
+
);
|
|
17118
|
+
if (!scrollElem) return;
|
|
17119
|
+
const containerRect = scrollElem.getBoundingClientRect();
|
|
17120
|
+
const categoryRect = categoryContainer.getBoundingClientRect();
|
|
17121
|
+
const headerHeight = header.offsetHeight;
|
|
17122
|
+
const categoryTop = categoryRect.top - containerRect.top + scrollElem.scrollTop;
|
|
17123
|
+
const categoryBottom = categoryTop + categoryContainer.offsetHeight;
|
|
17124
|
+
const sTop = scrollElem.scrollTop;
|
|
17125
|
+
const inView = sTop >= categoryTop - headerHeight && sTop < categoryBottom - headerHeight;
|
|
17126
|
+
setIsActive(inView);
|
|
17115
17127
|
};
|
|
17116
17128
|
elem.querySelector(".HOKKIEMOJIPICKER-emojidisplay").addEventListener("scroll", updateActive);
|
|
17117
17129
|
updateActive();
|
|
@@ -17119,28 +17131,37 @@ function SidebarCategory({
|
|
|
17119
17131
|
elem.querySelector(".HOKKIEMOJIPICKER-emojidisplay").removeEventListener("scroll", updateActive);
|
|
17120
17132
|
};
|
|
17121
17133
|
}, [picker, searchValue]);
|
|
17122
|
-
useEffect5(() => {
|
|
17123
|
-
const elem = picker.current;
|
|
17124
|
-
if (!elem) return;
|
|
17125
|
-
const header = elem.querySelector(
|
|
17126
|
-
".HOKKIEMOJIPICKER-categoryHeader." + categoryName
|
|
17127
|
-
);
|
|
17128
|
-
const categoryContainer = header.parentElement;
|
|
17129
|
-
const scrollTop2 = categoryContainer.offsetTop - header.offsetHeight * 5.5;
|
|
17130
|
-
setScrollTop(scrollTop2);
|
|
17131
|
-
}, []);
|
|
17132
17134
|
return /* @__PURE__ */ jsx5(
|
|
17133
17135
|
"button",
|
|
17134
17136
|
{
|
|
17135
17137
|
onClick: () => {
|
|
17136
17138
|
const elem = picker.current;
|
|
17139
|
+
if (!elem) return;
|
|
17140
|
+
const doScroll = () => {
|
|
17141
|
+
const header = elem.querySelector(
|
|
17142
|
+
".HOKKIEMOJIPICKER-categoryHeader." + categoryName
|
|
17143
|
+
);
|
|
17144
|
+
if (!header) return;
|
|
17145
|
+
const categoryContainer = header.parentElement;
|
|
17146
|
+
const scrollElem = elem.querySelector(
|
|
17147
|
+
".HOKKIEMOJIPICKER-emojidisplay"
|
|
17148
|
+
);
|
|
17149
|
+
if (!scrollElem) return;
|
|
17150
|
+
const containerRect = scrollElem.getBoundingClientRect();
|
|
17151
|
+
const categoryRect = categoryContainer.getBoundingClientRect();
|
|
17152
|
+
const headerHeight = header.offsetHeight;
|
|
17153
|
+
const targetTop = categoryRect.top - containerRect.top + scrollElem.scrollTop - headerHeight;
|
|
17154
|
+
scrollElem.scrollTo({
|
|
17155
|
+
top: Math.max(0, targetTop),
|
|
17156
|
+
behavior: "smooth"
|
|
17157
|
+
});
|
|
17158
|
+
};
|
|
17137
17159
|
if ((searchValue || "").length > 0) {
|
|
17138
17160
|
window["emojipicker-" + id].setSearchValue("");
|
|
17139
|
-
setTimeout(
|
|
17140
|
-
|
|
17141
|
-
|
|
17161
|
+
setTimeout(doScroll, 50);
|
|
17162
|
+
} else {
|
|
17163
|
+
doScroll();
|
|
17142
17164
|
}
|
|
17143
|
-
elem.querySelector(".HOKKIEMOJIPICKER-emojidisplay").scrollTop = scrollTop;
|
|
17144
17165
|
},
|
|
17145
17166
|
"data-active": isActive ? "true" : "false",
|
|
17146
17167
|
className: "!outline-0 HOKKIEMOJIPICKER-sidebarButton cursor-pointer size-8 data-[active=true]:bg-white/5 hover:bg-white/10 overflow-hidden *:!size-6.5 transition-all hover:*:!opacity-85 data-[active=true]:*:!opacity-100 flex items-center justify-center rounded-sm *:opacity-50",
|
|
@@ -17486,9 +17507,55 @@ function EmojiSelector({
|
|
|
17486
17507
|
...emojilib_default
|
|
17487
17508
|
].filter((a) => a !== void 0);
|
|
17488
17509
|
const navHeight = showNav ? Math.floor(height / 7) : 0;
|
|
17489
|
-
const id =
|
|
17510
|
+
const id = useMemo2(
|
|
17511
|
+
() => Math.random().toString(36).substring(2, 15),
|
|
17512
|
+
[]
|
|
17513
|
+
);
|
|
17490
17514
|
const picker = useRef2(null);
|
|
17491
|
-
|
|
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
|
+
);
|
|
17492
17559
|
return /* @__PURE__ */ jsx7("div", { className: "HOKKIEMOJIPICKER-emojiContainer", children: /* @__PURE__ */ jsxs6(
|
|
17493
17560
|
"div",
|
|
17494
17561
|
{
|
|
@@ -17556,6 +17623,9 @@ function EmojiSelector({
|
|
|
17556
17623
|
/* @__PURE__ */ jsx7(
|
|
17557
17624
|
"div",
|
|
17558
17625
|
{
|
|
17626
|
+
ref: scrollRef,
|
|
17627
|
+
tabIndex: 0,
|
|
17628
|
+
onKeyDown: handleKeyDown,
|
|
17559
17629
|
style: {
|
|
17560
17630
|
flexBasis: "fit-content"
|
|
17561
17631
|
},
|
|
@@ -17564,7 +17634,7 @@ function EmojiSelector({
|
|
|
17564
17634
|
if (!category) return;
|
|
17565
17635
|
const categoryInfo = categoryData[category.name];
|
|
17566
17636
|
return /* @__PURE__ */ jsx7(
|
|
17567
|
-
|
|
17637
|
+
categoryDisplay_default,
|
|
17568
17638
|
{
|
|
17569
17639
|
onEmojiMouseEnter,
|
|
17570
17640
|
onEmojiMouseLeave,
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hokkiai/discord-emoji-selector",
|
|
3
|
-
"version": "1.1.
|
|
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",
|
|
7
|
-
"homepage": "https://github.com/
|
|
7
|
+
"homepage": "https://github.com/hokkiai/discord-emoji-selector",
|
|
8
8
|
"module": "dist/index.js",
|
|
9
9
|
"types": "dist/index.d.ts",
|
|
10
10
|
"keywords": [
|
|
@@ -28,9 +28,6 @@
|
|
|
28
28
|
"files": [
|
|
29
29
|
"dist"
|
|
30
30
|
],
|
|
31
|
-
"scripts": {
|
|
32
|
-
"build": "tsup src/index.tsx --dts --format esm,cjs"
|
|
33
|
-
},
|
|
34
31
|
"peerDependencies": {
|
|
35
32
|
"react": ">=18"
|
|
36
33
|
},
|
|
@@ -44,5 +41,8 @@
|
|
|
44
41
|
"@types/react": "^19.1.10",
|
|
45
42
|
"tsup": "^8.5.0",
|
|
46
43
|
"typescript": "^5.9.2"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "tsup src/index.tsx --dts --format esm,cjs"
|
|
47
47
|
}
|
|
48
|
-
}
|
|
48
|
+
}
|