@hokkiai/discord-emoji-selector 1.1.7 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,205 @@
1
+ import {
2
+ render,
3
+ useSearchValue,
4
+ useSkin
5
+ } from "./chunk-DMO3WEXH.js";
6
+
7
+ // src/categoryDisplay.tsx
8
+ import { ChevronDown } from "lucide-react";
9
+ import { memo, useState, useCallback, useMemo } from "react";
10
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
11
+ var Emoji = memo(function Emoji2({
12
+ pickerId,
13
+ emoji,
14
+ onEmojiMouseEnter,
15
+ onEmojiMouseLeave,
16
+ onEmojiSelect
17
+ }) {
18
+ const handleMouseEnter = useCallback(() => {
19
+ window["emojipicker-" + pickerId].changeFooterEmoji(emoji);
20
+ window["emojipicker-" + pickerId].changeSearchbarPlaceholder(emoji.name);
21
+ onEmojiMouseEnter(emoji);
22
+ }, [pickerId, emoji, onEmojiMouseEnter]);
23
+ const handleMouseLeave = useCallback(() => {
24
+ onEmojiMouseLeave(emoji);
25
+ }, [emoji, onEmojiMouseLeave]);
26
+ const handleClick = useCallback(() => {
27
+ onEmojiSelect(emoji);
28
+ }, [emoji, onEmojiSelect]);
29
+ const html = useMemo(() => render(emoji.char), [emoji.char]);
30
+ return /* @__PURE__ */ jsx(
31
+ "div",
32
+ {
33
+ onMouseEnter: handleMouseEnter,
34
+ onMouseLeave: handleMouseLeave,
35
+ onClick: handleClick,
36
+ 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",
37
+ dangerouslySetInnerHTML: { __html: html }
38
+ }
39
+ );
40
+ });
41
+ var SkinEmoji = memo(function SkinEmoji2({
42
+ pickerId,
43
+ emoji,
44
+ onEmojiSelect,
45
+ onEmojiMouseEnter,
46
+ onEmojiMouseLeave
47
+ }) {
48
+ const skin = useSkin({ pickerId });
49
+ const fakeEmoji = useMemo(() => {
50
+ const charForSkin = [1, 2, 3, 4, 5].includes(skin) ? emoji.tones.find((a) => a.tone.find((b) => b === skin) === skin)?.char : emoji.char;
51
+ return {
52
+ ...emoji,
53
+ char: charForSkin,
54
+ preRendered: true,
55
+ tones: [
56
+ {
57
+ name: emoji.tones[0].name.replaceAll("1", "0"),
58
+ tone: [0],
59
+ char: emoji.char
60
+ },
61
+ ...emoji.tones
62
+ ]
63
+ };
64
+ }, [emoji, skin]);
65
+ const handleMouseEnter = useCallback(() => {
66
+ window["emojipicker-" + pickerId].changeFooterEmoji(fakeEmoji);
67
+ window["emojipicker-" + pickerId].changeSearchbarPlaceholder(emoji.name);
68
+ onEmojiMouseEnter(fakeEmoji);
69
+ }, [pickerId, fakeEmoji, emoji.name, onEmojiMouseEnter]);
70
+ const handleMouseLeave = useCallback(() => {
71
+ onEmojiMouseLeave(fakeEmoji);
72
+ }, [fakeEmoji, onEmojiMouseLeave]);
73
+ const handleClick = useCallback(() => {
74
+ onEmojiSelect(fakeEmoji);
75
+ }, [fakeEmoji, onEmojiSelect]);
76
+ const html = useMemo(() => render(fakeEmoji.char), [fakeEmoji.char]);
77
+ return /* @__PURE__ */ jsx(
78
+ "div",
79
+ {
80
+ onMouseEnter: handleMouseEnter,
81
+ onMouseLeave: handleMouseLeave,
82
+ onClick: handleClick,
83
+ 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",
84
+ dangerouslySetInnerHTML: { __html: html }
85
+ }
86
+ );
87
+ });
88
+ var CategoryDisplay = memo(function CategoryDisplay2({
89
+ category,
90
+ categoryInfo,
91
+ isToneSelectorEnabled,
92
+ onEmojiSelect,
93
+ onEmojiMouseEnter,
94
+ onEmojiMouseLeave,
95
+ pickerId
96
+ }) {
97
+ const storageKey = `hokkiemojipicker-category-${category.name}-open`;
98
+ const [isOpen, setIsOpen] = useState(() => {
99
+ const cached = localStorage?.getItem(storageKey);
100
+ return cached === null ? true : cached === "true";
101
+ });
102
+ const searchValue = useSearchValue({ pickerId });
103
+ const handleToggle = useCallback(() => {
104
+ setIsOpen((prev) => {
105
+ const newState = !prev;
106
+ try {
107
+ localStorage?.setItem(storageKey, String(newState));
108
+ } catch (e) {
109
+ }
110
+ return newState;
111
+ });
112
+ }, [storageKey]);
113
+ const searchLower = useMemo(
114
+ () => (searchValue || "").toLowerCase().replace(/_/g, " "),
115
+ [searchValue]
116
+ );
117
+ const filteredEmojis = useMemo(() => {
118
+ if (!searchLower) return null;
119
+ return category.emojis.filter(
120
+ (emoji) => emoji.name.toLowerCase().replace(/_/g, " ").includes(searchLower)
121
+ );
122
+ }, [category.emojis, searchLower]);
123
+ if (searchLower) {
124
+ if (category.name === "recentlyUsed")
125
+ return /* @__PURE__ */ jsx("div", { className: "h-1.5 w-full" });
126
+ return /* @__PURE__ */ jsx(Fragment, { children: filteredEmojis.map((emoji) => {
127
+ if (emoji.hasTone && !emoji.preRendered) {
128
+ return /* @__PURE__ */ jsx(
129
+ SkinEmoji,
130
+ {
131
+ onEmojiSelect,
132
+ pickerId,
133
+ onEmojiMouseEnter,
134
+ onEmojiMouseLeave,
135
+ emoji
136
+ },
137
+ emoji.name
138
+ );
139
+ }
140
+ return /* @__PURE__ */ jsx(
141
+ Emoji,
142
+ {
143
+ onEmojiSelect,
144
+ pickerId,
145
+ emoji,
146
+ onEmojiMouseEnter,
147
+ onEmojiMouseLeave
148
+ },
149
+ emoji.name
150
+ );
151
+ }) });
152
+ }
153
+ return /* @__PURE__ */ jsxs("div", { className: "HOKKIEMOJIPICKER-categorydisplay flex flex-col relative w-full pt-2", children: [
154
+ /* @__PURE__ */ jsx(
155
+ "div",
156
+ {
157
+ className: "HOKKIEMOJIPICKER-categoryHeader sticky top-0 pt-2 cursor-pointer text-white px-2 flex bg-[#131416] p-1 pb-2 " + category.name,
158
+ onClick: handleToggle,
159
+ children: /* @__PURE__ */ jsxs("span", { className: "flex gap-1.5 items-center opacity-75 hover:opacity-100", children: [
160
+ /* @__PURE__ */ jsx("span", { className: "*:size-4.5", children: categoryInfo.icon }),
161
+ " ",
162
+ /* @__PURE__ */ jsx("span", { className: "text-md font-semibold font-gg", children: categoryInfo.name }),
163
+ /* @__PURE__ */ jsx(
164
+ ChevronDown,
165
+ {
166
+ strokeWidth: 2,
167
+ className: "transition-all size-5 data-[open=false]:-rotate-90",
168
+ "data-open": isOpen ? "true" : "false"
169
+ }
170
+ )
171
+ ] })
172
+ }
173
+ ),
174
+ isOpen && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-y-0.5", children: category.emojis.map((emoji) => {
175
+ if (emoji.hasTone && !emoji.preRendered && isToneSelectorEnabled) {
176
+ return /* @__PURE__ */ jsx(
177
+ SkinEmoji,
178
+ {
179
+ onEmojiSelect,
180
+ pickerId,
181
+ emoji,
182
+ onEmojiMouseEnter,
183
+ onEmojiMouseLeave
184
+ },
185
+ emoji.name
186
+ );
187
+ }
188
+ return /* @__PURE__ */ jsx(
189
+ Emoji,
190
+ {
191
+ onEmojiSelect,
192
+ pickerId,
193
+ onEmojiMouseEnter,
194
+ onEmojiMouseLeave,
195
+ emoji
196
+ },
197
+ emoji.name
198
+ );
199
+ }) })
200
+ ] });
201
+ });
202
+ var categoryDisplay_default = CategoryDisplay;
203
+ export {
204
+ categoryDisplay_default as default
205
+ };
package/dist/index.cjs CHANGED
@@ -186,15 +186,22 @@ var init_categoryDisplay = __esm({
186
186
  onEmojiMouseLeave,
187
187
  pickerId
188
188
  }) {
189
- const [isOpen, setIsOpen] = (0, import_react6.useState)(true);
189
+ const storageKey = `hokkiemojipicker-category-${category.name}-open`;
190
+ const [isOpen, setIsOpen] = (0, import_react6.useState)(() => {
191
+ const cached = localStorage?.getItem(storageKey);
192
+ return cached === null ? true : cached === "true";
193
+ });
190
194
  const searchValue = useSearchValue({ pickerId });
191
- (0, import_react6.useEffect)(() => {
192
- setIsOpen(
193
- (localStorage.getItem(
194
- "hokkiemojipicker-category-" + category.name + "-open"
195
- ) || "true") === "true"
196
- );
197
- }, [category.name]);
195
+ const handleToggle = (0, import_react6.useCallback)(() => {
196
+ setIsOpen((prev) => {
197
+ const newState = !prev;
198
+ try {
199
+ localStorage?.setItem(storageKey, String(newState));
200
+ } catch (e) {
201
+ }
202
+ return newState;
203
+ });
204
+ }, [storageKey]);
198
205
  const searchLower = (0, import_react6.useMemo)(
199
206
  () => (searchValue || "").toLowerCase().replace(/_/g, " "),
200
207
  [searchValue]
@@ -240,14 +247,7 @@ var init_categoryDisplay = __esm({
240
247
  "div",
241
248
  {
242
249
  className: "HOKKIEMOJIPICKER-categoryHeader sticky top-0 pt-2 cursor-pointer text-white px-2 flex bg-[#131416] p-1 pb-2 " + category.name,
243
- onClick: () => {
244
- const newOpen = !isOpen;
245
- setIsOpen(newOpen);
246
- localStorage.setItem(
247
- "hokkiemojipicker-category-" + category.name + "-open",
248
- newOpen ? "true" : "false"
249
- );
250
- },
250
+ onClick: handleToggle,
251
251
  children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "flex gap-1.5 items-center opacity-75 hover:opacity-100", children: [
252
252
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "*:size-4.5", children: categoryInfo.icon }),
253
253
  " ",
@@ -368,7 +368,7 @@ function SearchBar({
368
368
  "div",
369
369
  {
370
370
  "data-focused": isFocused ? "true" : "false",
371
- className: "bg-[#121214] items-center w-full border-1 border-[#323337] p-2 px-3 flex rounded-md data-[focused=true]:border-[#3687E9]",
371
+ className: "bg-white/5 items-center w-full border-1 border-[#323337] p-2 px-3 flex rounded-md data-[focused=true]:border-[#3687E9]",
372
372
  children: [
373
373
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
374
374
  "input",
@@ -401,7 +401,24 @@ function Footer({
401
401
  setFooterEmoji(emoji);
402
402
  };
403
403
  }, []);
404
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "HOKKIEMOJIPICKER-footer mt-auto flex gap-2 items-center border-t-1 px-4 py-3 border-[#363639] bg-[#070709]", children: [
404
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "HOKKIEMOJIPICKER-footer relative mt-auto flex gap-2 items-center px-4 py-3 bg-[#070709]", children: [
405
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
406
+ "div",
407
+ {
408
+ style: {
409
+ cssText: `
410
+ left: 0;
411
+ position: absolute;
412
+ background-color: transparent;
413
+ top: 0;
414
+ width: 12px;
415
+ height: 12px;
416
+ transform: translateY(-100%);
417
+ border-radius: 100%;
418
+ box-shadow: -14px 14px 0px 10px #070709;`
419
+ }
420
+ }
421
+ ),
405
422
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
406
423
  "div",
407
424
  {
@@ -501,26 +518,39 @@ var import_react5 = require("react");
501
518
  init_render();
502
519
  var import_jsx_runtime5 = require("react/jsx-runtime");
503
520
  function SkinSelector({ id }) {
504
- const skins = ["\u{1F44F}", "\u{1F44F}\u{1F3FB}", "\u{1F44F}\u{1F3FC}", "\u{1F44F}\u{1F3FD}", "\u{1F44F}\u{1F3FE}", "\u{1F44F}\u{1F3FF}"];
505
- const [selectedTone, setSelectedTone] = (0, import_react5.useState)(0);
521
+ const skins = (0, import_react5.useMemo)(() => ["\u{1F44F}", "\u{1F44F}\u{1F3FB}", "\u{1F44F}\u{1F3FC}", "\u{1F44F}\u{1F3FD}", "\u{1F44F}\u{1F3FE}", "\u{1F44F}\u{1F3FF}"], []);
522
+ const [selectedTone, setSelectedTone] = (0, import_react5.useState)(() => {
523
+ const cached = localStorage?.getItem("hokkiemojipicker-skin");
524
+ return cached ? parseInt(cached, 10) : 0;
525
+ });
506
526
  const [open, setOpen] = (0, import_react5.useState)(false);
507
- (0, import_react5.useEffect)(() => {
508
- setSelectedTone(
509
- parseInt(localStorage.getItem("hokkiemojipicker-skin") || "0")
510
- );
511
- }, []);
512
527
  (0, import_react5.useEffect)(() => {
513
528
  window["emojipicker-" + id].skin = selectedTone;
514
- }, [selectedTone]);
529
+ }, [selectedTone, id]);
530
+ const toggleOpen = (0, import_react5.useCallback)(() => setOpen((prev) => !prev), []);
531
+ const handleSkinSelect = (0, import_react5.useCallback)((i) => {
532
+ setOpen(false);
533
+ setSelectedTone(i);
534
+ try {
535
+ localStorage?.setItem("hokkiemojipicker-skin", String(i));
536
+ } catch (e) {
537
+ }
538
+ }, []);
539
+ const renderedCurrentSkin = (0, import_react5.useMemo)(
540
+ () => render(skins[selectedTone]),
541
+ [skins, selectedTone]
542
+ );
543
+ const availableSkins = (0, import_react5.useMemo)(
544
+ () => skins.map((skin, i) => skin === skins[selectedTone] ? null : { skin, i }).filter(Boolean),
545
+ [skins, selectedTone]
546
+ );
515
547
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "relative mr-1", children: [
516
548
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
517
549
  "div",
518
550
  {
519
551
  className: "HOKKIEMOJIPICKER-skinselector-trigger opacity-75 hover:opacity-100 cursor-pointer *:size-7 *:min-w-7",
520
- onClick: () => {
521
- setOpen(!open);
522
- },
523
- dangerouslySetInnerHTML: { __html: render(skins[selectedTone]) }
552
+ onClick: toggleOpen,
553
+ dangerouslySetInnerHTML: { __html: renderedCurrentSkin }
524
554
  }
525
555
  ),
526
556
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
@@ -531,20 +561,18 @@ function SkinSelector({ id }) {
531
561
  pointerEvents: open ? "all" : "none"
532
562
  },
533
563
  className: "absolute flex transition-all flex-col cursor-pointer top-full translate-y-2 overflow-hidden -left-2 rounded-sm border-1 bg-neutral-900",
534
- children: skins.filter((a) => a !== skins[selectedTone]).map((skin, i) => {
564
+ children: availableSkins.map((item) => {
565
+ const { skin, i } = item;
535
566
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
536
567
  "div",
537
568
  {
538
569
  className: "HOKKIEMOJIPICKER-skinselector-skinoption *:size-7 *:min-w-7 hover:bg-white/5 p-2 transition-all",
539
- onClick: () => {
540
- setOpen(!open);
541
- setSelectedTone(i);
542
- localStorage.setItem("hokkiemojipicker-skin", i + "");
543
- },
570
+ onClick: () => handleSkinSelect(i),
544
571
  dangerouslySetInnerHTML: {
545
572
  __html: render(skin)
546
573
  }
547
- }
574
+ },
575
+ i
548
576
  );
549
577
  })
550
578
  }
@@ -17611,7 +17639,7 @@ function EmojiSelector({
17611
17639
  showNav && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
17612
17640
  "div",
17613
17641
  {
17614
- className: "HOKKIEMOJIPICKER-nav border-[#363639] flex items-center gap-4 border-b-1 p-3 relative z-50",
17642
+ className: "HOKKIEMOJIPICKER-nav flex items-center gap-4 p-3 relative z-50",
17615
17643
  style: {
17616
17644
  height: navHeight,
17617
17645
  maxHeight: navHeight,
@@ -17624,7 +17652,7 @@ function EmojiSelector({
17624
17652
  }
17625
17653
  ),
17626
17654
  /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex h-full", children: [
17627
- showSidebar && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "HOKKIEMOJIPICKER-sidebar bg-[#070709] flex p-2 gap-1 flex-col", children: [
17655
+ showSidebar && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "HOKKIEMOJIPICKER-sidebar rounded-t-md bg-[#070709] flex p-2 gap-1 flex-col", children: [
17628
17656
  Object.keys(categoryData).filter((categoryName) => categoryData[categoryName] !== false).map((categoryName) => {
17629
17657
  const category = categoryData[categoryName];
17630
17658
  if (category === true) {
@@ -17662,38 +17690,49 @@ function EmojiSelector({
17662
17690
  flexBasis: "fit-content"
17663
17691
  },
17664
17692
  className: "HOKKIEMOJIPICKER-emojidisplay overflow-y-scroll bg-[#131416] h-full w-full flex flex-wrap px-2 items-start justfy-start justify-self-start gap-y-0.5",
17665
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react7.Suspense, { fallback: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", {}), children: emojis.map((category) => {
17666
- if (!category) return;
17667
- const categoryInfo = categoryData[category.name];
17668
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
17669
- CategoryDisplay3,
17670
- {
17671
- onEmojiMouseEnter,
17672
- onEmojiMouseLeave,
17673
- isToneSelectorEnabled: toneSelector,
17674
- onEmojiSelect: (a) => {
17675
- onEmojiSelect(a);
17676
- if (a.char.startsWith("<")) return;
17677
- let newRecentlyUsed = JSON.parse(
17678
- localStorage.getItem("hokkiemojipicker-recentlyused") || "[]"
17679
- );
17680
- newRecentlyUsed = newRecentlyUsed.filter(
17681
- (b) => b.char !== a.char
17682
- );
17683
- newRecentlyUsed.push(a);
17684
- newRecentlyUsed = newRecentlyUsed.slice(0, 20);
17685
- localStorage.setItem(
17686
- "hokkiemojipicker-recentlyused",
17687
- JSON.stringify(newRecentlyUsed)
17688
- );
17689
- },
17690
- category,
17691
- pickerId: id,
17692
- categoryInfo
17693
- },
17694
- category.name
17695
- );
17696
- }) })
17693
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
17694
+ import_react7.Suspense,
17695
+ {
17696
+ fallback: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "w-full h-full flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "flex flex-col items-center gap-3", children: [
17697
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "animate-spin size-8 border-2 border-white/20 border-t-white/80 rounded-full" }),
17698
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "text-white/60 text-sm", children: "Loading emojis..." })
17699
+ ] }) }),
17700
+ children: emojis.map((category) => {
17701
+ if (!category) return;
17702
+ const categoryInfo = categoryData[category.name];
17703
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
17704
+ CategoryDisplay3,
17705
+ {
17706
+ onEmojiMouseEnter,
17707
+ onEmojiMouseLeave,
17708
+ isToneSelectorEnabled: toneSelector,
17709
+ onEmojiSelect: (a) => {
17710
+ onEmojiSelect(a);
17711
+ if (a.char.startsWith("<")) return;
17712
+ let newRecentlyUsed = JSON.parse(
17713
+ localStorage.getItem(
17714
+ "hokkiemojipicker-recentlyused"
17715
+ ) || "[]"
17716
+ );
17717
+ newRecentlyUsed = newRecentlyUsed.filter(
17718
+ (b) => b.char !== a.char
17719
+ );
17720
+ newRecentlyUsed.push(a);
17721
+ newRecentlyUsed = newRecentlyUsed.slice(0, 20);
17722
+ localStorage.setItem(
17723
+ "hokkiemojipicker-recentlyused",
17724
+ JSON.stringify(newRecentlyUsed)
17725
+ );
17726
+ },
17727
+ category,
17728
+ pickerId: id,
17729
+ categoryInfo
17730
+ },
17731
+ category.name
17732
+ );
17733
+ })
17734
+ }
17735
+ )
17697
17736
  }
17698
17737
  ),
17699
17738
  showFooter && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Footer, { id, firstEmoji: emojis[0].emojis[0] })
package/dist/index.css CHANGED
@@ -36,6 +36,10 @@
36
36
  width: calc(var(--spacing) * 5);
37
37
  height: calc(var(--spacing) * 5);
38
38
  }
39
+ .HOKKIEMOJIPICKER-emojiContainer .size-8 {
40
+ width: calc(var(--spacing) * 8);
41
+ height: calc(var(--spacing) * 8);
42
+ }
39
43
  .HOKKIEMOJIPICKER-emojiContainer .size-12\.5 {
40
44
  width: calc(var(--spacing) * 12.5);
41
45
  height: calc(var(--spacing) * 12.5);
@@ -86,6 +90,9 @@
86
90
  .HOKKIEMOJIPICKER-emojiContainer .gap-2 {
87
91
  gap: calc(var(--spacing) * 2);
88
92
  }
93
+ .HOKKIEMOJIPICKER-emojiContainer .gap-3 {
94
+ gap: calc(var(--spacing) * 3);
95
+ }
89
96
  .HOKKIEMOJIPICKER-emojiContainer .gap-4 {
90
97
  gap: calc(var(--spacing) * 4);
91
98
  }
@@ -101,12 +108,19 @@
101
108
  .HOKKIEMOJIPICKER-emojiContainer .overflow-y-scroll {
102
109
  overflow-y: scroll;
103
110
  }
111
+ .HOKKIEMOJIPICKER-emojiContainer .rounded-t-md {
112
+ border-top-left-radius: var(--radius-md);
113
+ border-top-right-radius: var(--radius-md);
114
+ }
104
115
  .HOKKIEMOJIPICKER-emojiContainer .rounded-md {
105
116
  border-radius: var(--radius-md);
106
117
  }
107
118
  .HOKKIEMOJIPICKER-emojiContainer .rounded-sm {
108
119
  border-radius: var(--radius-sm);
109
120
  }
121
+ .HOKKIEMOJIPICKER-emojiContainer .rounded-full {
122
+ border-radius: 9999px;
123
+ }
110
124
  .HOKKIEMOJIPICKER-emojiContainer .rounded-xl {
111
125
  border-radius: var(--radius-xl);
112
126
  }
@@ -114,6 +128,10 @@
114
128
  border-style: var(--tw-border-style);
115
129
  border-width: 1px;
116
130
  }
131
+ .HOKKIEMOJIPICKER-emojiContainer .border-2 {
132
+ border-style: var(--tw-border-style);
133
+ border-width: 2px;
134
+ }
117
135
  .HOKKIEMOJIPICKER-emojiContainer .border-t-1 {
118
136
  border-top-style: var(--tw-border-style);
119
137
  border-top-width: 1px;
@@ -131,6 +149,12 @@
131
149
  .HOKKIEMOJIPICKER-emojiContainer .border-\[\#363639\]\/10 {
132
150
  border-color: color-mix(in oklab, #363639 10%, transparent);
133
151
  }
152
+ .HOKKIEMOJIPICKER-emojiContainer .border-white\/20 {
153
+ border-color: color-mix(in oklab, var(--color-white) 20%, transparent);
154
+ }
155
+ .HOKKIEMOJIPICKER-emojiContainer .border-t-white\/80 {
156
+ border-top-color: color-mix(in oklab, var(--color-white) 80%, transparent);
157
+ }
134
158
  .HOKKIEMOJIPICKER-emojiContainer .bg-\[\#070709\] {
135
159
  background-color: #070709;
136
160
  }
@@ -174,6 +198,10 @@
174
198
  font-size: var(--text-4xl);
175
199
  line-height: var(--tw-leading, var(--text-4xl--line-height));
176
200
  }
201
+ .HOKKIEMOJIPICKER-emojiContainer .text-sm {
202
+ font-size: 0.875rem;
203
+ line-height: 1.25rem;
204
+ }
177
205
  .HOKKIEMOJIPICKER-emojiContainer .text-lg {
178
206
  font-size: var(--text-lg);
179
207
  line-height: var(--tw-leading, var(--text-lg--line-height));
@@ -191,6 +219,12 @@
191
219
  .HOKKIEMOJIPICKER-emojiContainer .text-white {
192
220
  color: var(--color-white);
193
221
  }
222
+ .HOKKIEMOJIPICKER-emojiContainer .text-white\/60 {
223
+ color: color-mix(in oklab, var(--color-white) 60%, transparent);
224
+ }
225
+ .HOKKIEMOJIPICKER-emojiContainer .text-white\/80 {
226
+ color: color-mix(in oklab, var(--color-white) 80%, transparent);
227
+ }
194
228
  .HOKKIEMOJIPICKER-emojiContainer .opacity-50 {
195
229
  opacity: 50%;
196
230
  }
@@ -239,6 +273,12 @@
239
273
  min-width: calc(var(--spacing) * 9);
240
274
  }
241
275
  }
276
+ .HOKKIEMOJIPICKER-emojiContainer .bg-white\/5 {
277
+ background-color: color-mix(in srgb, #fff 5%, transparent);
278
+ @supports (color: color-mix(in lab, red, red)) {
279
+ background-color: color-mix(in oklab, var(--color-white) 5%, transparent);
280
+ }
281
+ }
242
282
  .HOKKIEMOJIPICKER-emojiContainer .hover\:bg-white\/5 {
243
283
  &:hover {
244
284
  @media (hover: hover) {
@@ -276,6 +316,17 @@
276
316
  rotate: calc(90deg * -1);
277
317
  }
278
318
  }
319
+ .HOKKIEMOJIPICKER-emojiContainer .animate-spin {
320
+ animation: HOKKIEMOJIPICKER-spin 1s linear infinite;
321
+ }
322
+ @keyframes HOKKIEMOJIPICKER-spin {
323
+ from {
324
+ transform: rotate(0deg);
325
+ }
326
+ to {
327
+ transform: rotate(360deg);
328
+ }
329
+ }
279
330
  .HOKKIEMOJIPICKER-emojiContainer * {
280
331
  --spacing: 0.25rem;
281
332
  --text-lg: 1.125rem;
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-DMO3WEXH.js";
5
5
 
6
6
  // src/index.tsx
7
- import { lazy, Suspense, useCallback, useEffect as useEffect5, useMemo, useRef as useRef2 } from "react";
7
+ import { lazy, Suspense, useCallback as useCallback2, useEffect as useEffect5, useMemo as useMemo2, useRef as useRef2 } from "react";
8
8
 
9
9
  // src/searchbar.tsx
10
10
  import { useEffect, useRef, useState } from "react";
@@ -70,7 +70,7 @@ function SearchBar({
70
70
  "div",
71
71
  {
72
72
  "data-focused": isFocused ? "true" : "false",
73
- className: "bg-[#121214] items-center w-full border-1 border-[#323337] p-2 px-3 flex rounded-md data-[focused=true]:border-[#3687E9]",
73
+ className: "bg-white/5 items-center w-full border-1 border-[#323337] p-2 px-3 flex rounded-md data-[focused=true]:border-[#3687E9]",
74
74
  children: [
75
75
  /* @__PURE__ */ jsx2(
76
76
  "input",
@@ -102,7 +102,24 @@ function Footer({
102
102
  setFooterEmoji(emoji);
103
103
  };
104
104
  }, []);
105
- return /* @__PURE__ */ jsxs3("div", { className: "HOKKIEMOJIPICKER-footer mt-auto flex gap-2 items-center border-t-1 px-4 py-3 border-[#363639] bg-[#070709]", children: [
105
+ return /* @__PURE__ */ jsxs3("div", { className: "HOKKIEMOJIPICKER-footer relative mt-auto flex gap-2 items-center px-4 py-3 bg-[#070709]", children: [
106
+ /* @__PURE__ */ jsx3(
107
+ "div",
108
+ {
109
+ style: {
110
+ cssText: `
111
+ left: 0;
112
+ position: absolute;
113
+ background-color: transparent;
114
+ top: 0;
115
+ width: 12px;
116
+ height: 12px;
117
+ transform: translateY(-100%);
118
+ border-radius: 100%;
119
+ box-shadow: -14px 14px 0px 10px #070709;`
120
+ }
121
+ }
122
+ ),
106
123
  /* @__PURE__ */ jsx3(
107
124
  "div",
108
125
  {
@@ -197,29 +214,42 @@ function SidebarCategory({
197
214
  }
198
215
 
199
216
  // src/skinselector.tsx
200
- import { useEffect as useEffect4, useState as useState4 } from "react";
217
+ import { useCallback, useEffect as useEffect4, useMemo, useState as useState4 } from "react";
201
218
  import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
202
219
  function SkinSelector({ id }) {
203
- const skins = ["\u{1F44F}", "\u{1F44F}\u{1F3FB}", "\u{1F44F}\u{1F3FC}", "\u{1F44F}\u{1F3FD}", "\u{1F44F}\u{1F3FE}", "\u{1F44F}\u{1F3FF}"];
204
- const [selectedTone, setSelectedTone] = useState4(0);
220
+ const skins = useMemo(() => ["\u{1F44F}", "\u{1F44F}\u{1F3FB}", "\u{1F44F}\u{1F3FC}", "\u{1F44F}\u{1F3FD}", "\u{1F44F}\u{1F3FE}", "\u{1F44F}\u{1F3FF}"], []);
221
+ const [selectedTone, setSelectedTone] = useState4(() => {
222
+ const cached = localStorage?.getItem("hokkiemojipicker-skin");
223
+ return cached ? parseInt(cached, 10) : 0;
224
+ });
205
225
  const [open, setOpen] = useState4(false);
206
- useEffect4(() => {
207
- setSelectedTone(
208
- parseInt(localStorage.getItem("hokkiemojipicker-skin") || "0")
209
- );
210
- }, []);
211
226
  useEffect4(() => {
212
227
  window["emojipicker-" + id].skin = selectedTone;
213
- }, [selectedTone]);
228
+ }, [selectedTone, id]);
229
+ const toggleOpen = useCallback(() => setOpen((prev) => !prev), []);
230
+ const handleSkinSelect = useCallback((i) => {
231
+ setOpen(false);
232
+ setSelectedTone(i);
233
+ try {
234
+ localStorage?.setItem("hokkiemojipicker-skin", String(i));
235
+ } catch (e) {
236
+ }
237
+ }, []);
238
+ const renderedCurrentSkin = useMemo(
239
+ () => render(skins[selectedTone]),
240
+ [skins, selectedTone]
241
+ );
242
+ const availableSkins = useMemo(
243
+ () => skins.map((skin, i) => skin === skins[selectedTone] ? null : { skin, i }).filter(Boolean),
244
+ [skins, selectedTone]
245
+ );
214
246
  return /* @__PURE__ */ jsxs4("div", { className: "relative mr-1", children: [
215
247
  /* @__PURE__ */ jsx5(
216
248
  "div",
217
249
  {
218
250
  className: "HOKKIEMOJIPICKER-skinselector-trigger opacity-75 hover:opacity-100 cursor-pointer *:size-7 *:min-w-7",
219
- onClick: () => {
220
- setOpen(!open);
221
- },
222
- dangerouslySetInnerHTML: { __html: render(skins[selectedTone]) }
251
+ onClick: toggleOpen,
252
+ dangerouslySetInnerHTML: { __html: renderedCurrentSkin }
223
253
  }
224
254
  ),
225
255
  /* @__PURE__ */ jsx5(
@@ -230,20 +260,18 @@ function SkinSelector({ id }) {
230
260
  pointerEvents: open ? "all" : "none"
231
261
  },
232
262
  className: "absolute flex transition-all flex-col cursor-pointer top-full translate-y-2 overflow-hidden -left-2 rounded-sm border-1 bg-neutral-900",
233
- children: skins.filter((a) => a !== skins[selectedTone]).map((skin, i) => {
263
+ children: availableSkins.map((item) => {
264
+ const { skin, i } = item;
234
265
  return /* @__PURE__ */ jsx5(
235
266
  "div",
236
267
  {
237
268
  className: "HOKKIEMOJIPICKER-skinselector-skinoption *:size-7 *:min-w-7 hover:bg-white/5 p-2 transition-all",
238
- onClick: () => {
239
- setOpen(!open);
240
- setSelectedTone(i);
241
- localStorage.setItem("hokkiemojipicker-skin", i + "");
242
- },
269
+ onClick: () => handleSkinSelect(i),
243
270
  dangerouslySetInnerHTML: {
244
271
  __html: render(skin)
245
272
  }
246
- }
273
+ },
274
+ i
247
275
  );
248
276
  })
249
277
  }
@@ -16990,7 +17018,7 @@ var emojilib_default = [
16990
17018
 
16991
17019
  // src/index.tsx
16992
17020
  import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
16993
- var CategoryDisplay = lazy(() => import("./categoryDisplay-BV7OPJVG.js"));
17021
+ var CategoryDisplay = lazy(() => import("./categoryDisplay-AZPE23IJ.js"));
16994
17022
  function EmojiSelector({
16995
17023
  categories = {},
16996
17024
  customEmojis = {},
@@ -17009,7 +17037,7 @@ function EmojiSelector({
17009
17037
  height = 500,
17010
17038
  width = 520
17011
17039
  }) {
17012
- const recentlyUsed = useMemo(
17040
+ const recentlyUsed = useMemo2(
17013
17041
  () => JSON.parse(
17014
17042
  (localStorage || { getItem: (a) => "[]" }).getItem(
17015
17043
  "hokkiemojipicker-recentlyused"
@@ -17253,7 +17281,7 @@ function EmojiSelector({
17253
17281
  ...categories.flags
17254
17282
  }
17255
17283
  };
17256
- const emojis = useMemo(() => {
17284
+ const emojis = useMemo2(() => {
17257
17285
  return [
17258
17286
  recentlyUsed.length > 0 ? {
17259
17287
  name: "recentlyUsed",
@@ -17269,10 +17297,10 @@ function EmojiSelector({
17269
17297
  ].filter((a) => a !== void 0);
17270
17298
  }, [recentlyUsed, customEmojis]);
17271
17299
  const navHeight = showNav ? Math.floor(height / 7) : 0;
17272
- const id = useMemo(() => Math.random().toString(36).substring(2, 15), []);
17300
+ const id = useMemo2(() => Math.random().toString(36).substring(2, 15), []);
17273
17301
  const picker = useRef2(null);
17274
17302
  const scrollRef = useRef2(null);
17275
- const handleWheel = useCallback((e) => {
17303
+ const handleWheel = useCallback2((e) => {
17276
17304
  const el = scrollRef.current;
17277
17305
  if (!el) return;
17278
17306
  e.preventDefault();
@@ -17307,7 +17335,7 @@ function EmojiSelector({
17307
17335
  showNav && /* @__PURE__ */ jsxs5(
17308
17336
  "div",
17309
17337
  {
17310
- className: "HOKKIEMOJIPICKER-nav border-[#363639] flex items-center gap-4 border-b-1 p-3 relative z-50",
17338
+ className: "HOKKIEMOJIPICKER-nav flex items-center gap-4 p-3 relative z-50",
17311
17339
  style: {
17312
17340
  height: navHeight,
17313
17341
  maxHeight: navHeight,
@@ -17320,7 +17348,7 @@ function EmojiSelector({
17320
17348
  }
17321
17349
  ),
17322
17350
  /* @__PURE__ */ jsxs5("div", { className: "flex h-full", children: [
17323
- showSidebar && /* @__PURE__ */ jsxs5("div", { className: "HOKKIEMOJIPICKER-sidebar bg-[#070709] flex p-2 gap-1 flex-col", children: [
17351
+ showSidebar && /* @__PURE__ */ jsxs5("div", { className: "HOKKIEMOJIPICKER-sidebar rounded-t-md bg-[#070709] flex p-2 gap-1 flex-col", children: [
17324
17352
  Object.keys(categoryData).filter((categoryName) => categoryData[categoryName] !== false).map((categoryName) => {
17325
17353
  const category = categoryData[categoryName];
17326
17354
  if (category === true) {
@@ -17358,38 +17386,49 @@ function EmojiSelector({
17358
17386
  flexBasis: "fit-content"
17359
17387
  },
17360
17388
  className: "HOKKIEMOJIPICKER-emojidisplay overflow-y-scroll bg-[#131416] h-full w-full flex flex-wrap px-2 items-start justfy-start justify-self-start gap-y-0.5",
17361
- children: /* @__PURE__ */ jsx6(Suspense, { fallback: /* @__PURE__ */ jsx6("div", {}), children: emojis.map((category) => {
17362
- if (!category) return;
17363
- const categoryInfo = categoryData[category.name];
17364
- return /* @__PURE__ */ jsx6(
17365
- CategoryDisplay,
17366
- {
17367
- onEmojiMouseEnter,
17368
- onEmojiMouseLeave,
17369
- isToneSelectorEnabled: toneSelector,
17370
- onEmojiSelect: (a) => {
17371
- onEmojiSelect(a);
17372
- if (a.char.startsWith("<")) return;
17373
- let newRecentlyUsed = JSON.parse(
17374
- localStorage.getItem("hokkiemojipicker-recentlyused") || "[]"
17375
- );
17376
- newRecentlyUsed = newRecentlyUsed.filter(
17377
- (b) => b.char !== a.char
17378
- );
17379
- newRecentlyUsed.push(a);
17380
- newRecentlyUsed = newRecentlyUsed.slice(0, 20);
17381
- localStorage.setItem(
17382
- "hokkiemojipicker-recentlyused",
17383
- JSON.stringify(newRecentlyUsed)
17384
- );
17385
- },
17386
- category,
17387
- pickerId: id,
17388
- categoryInfo
17389
- },
17390
- category.name
17391
- );
17392
- }) })
17389
+ children: /* @__PURE__ */ jsx6(
17390
+ Suspense,
17391
+ {
17392
+ fallback: /* @__PURE__ */ jsx6("div", { className: "w-full h-full flex items-center justify-center", children: /* @__PURE__ */ jsxs5("div", { className: "flex flex-col items-center gap-3", children: [
17393
+ /* @__PURE__ */ jsx6("div", { className: "animate-spin size-8 border-2 border-white/20 border-t-white/80 rounded-full" }),
17394
+ /* @__PURE__ */ jsx6("span", { className: "text-white/60 text-sm", children: "Loading emojis..." })
17395
+ ] }) }),
17396
+ children: emojis.map((category) => {
17397
+ if (!category) return;
17398
+ const categoryInfo = categoryData[category.name];
17399
+ return /* @__PURE__ */ jsx6(
17400
+ CategoryDisplay,
17401
+ {
17402
+ onEmojiMouseEnter,
17403
+ onEmojiMouseLeave,
17404
+ isToneSelectorEnabled: toneSelector,
17405
+ onEmojiSelect: (a) => {
17406
+ onEmojiSelect(a);
17407
+ if (a.char.startsWith("<")) return;
17408
+ let newRecentlyUsed = JSON.parse(
17409
+ localStorage.getItem(
17410
+ "hokkiemojipicker-recentlyused"
17411
+ ) || "[]"
17412
+ );
17413
+ newRecentlyUsed = newRecentlyUsed.filter(
17414
+ (b) => b.char !== a.char
17415
+ );
17416
+ newRecentlyUsed.push(a);
17417
+ newRecentlyUsed = newRecentlyUsed.slice(0, 20);
17418
+ localStorage.setItem(
17419
+ "hokkiemojipicker-recentlyused",
17420
+ JSON.stringify(newRecentlyUsed)
17421
+ );
17422
+ },
17423
+ category,
17424
+ pickerId: id,
17425
+ categoryInfo
17426
+ },
17427
+ category.name
17428
+ );
17429
+ })
17430
+ }
17431
+ )
17393
17432
  }
17394
17433
  ),
17395
17434
  showFooter && /* @__PURE__ */ jsx6(Footer, { id, firstEmoji: emojis[0].emojis[0] })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hokkiai/discord-emoji-selector",
3
- "version": "1.1.7",
3
+ "version": "1.2.0",
4
4
  "description": "A lightweight & powerful discord-style emoji picker",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",