@choice-ui/react 1.7.8 → 1.8.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.
@@ -15,12 +15,24 @@ type VirtualItem = {
15
15
  emojis: EmojiData[];
16
16
  type: "emojis";
17
17
  };
18
+ interface CategoryNames {
19
+ activities: string;
20
+ animalsNature: string;
21
+ flags: string;
22
+ foodDrink: string;
23
+ frequentlyUsed: string;
24
+ objects: string;
25
+ smileysPeople: string;
26
+ symbols: string;
27
+ travelPlaces: string;
28
+ }
18
29
  interface UseEmojiDataProps {
30
+ categoryNames?: CategoryNames;
19
31
  columns: number;
20
32
  searchQuery: string;
21
33
  showFrequentlyUsed: boolean;
22
34
  }
23
- declare function useEmojiData({ searchQuery, columns, showFrequentlyUsed }: UseEmojiDataProps): {
35
+ declare function useEmojiData({ searchQuery, columns, showFrequentlyUsed, categoryNames, }: UseEmojiDataProps): {
24
36
  categorizedData: VirtualItem[];
25
37
  categoryIndexMap: Map<EmojiCategory, number>;
26
38
  searchResults: {
@@ -52,6 +64,22 @@ interface EmojiPickerProps {
52
64
  showFooter?: boolean;
53
65
  value?: EmojiData | null;
54
66
  variant?: "default" | "dark" | "light";
67
+ i18n?: {
68
+ noEmojisFoundTitle?: string;
69
+ noEmojisFoundDescription?: string;
70
+ footerPickAnEmoji?: string;
71
+ categories?: {
72
+ frequentlyUsed: string;
73
+ smileysPeople: string;
74
+ animalsNature: string;
75
+ foodDrink: string;
76
+ travelPlaces: string;
77
+ activities: string;
78
+ objects: string;
79
+ symbols: string;
80
+ flags: string;
81
+ };
82
+ };
55
83
  }
56
84
  declare const EmojiPicker: React.NamedExoticComponent<EmojiPickerProps>;
57
85
 
@@ -141,7 +141,7 @@ var emojiFooterTv = tcv({
141
141
  var emojiEmptyTv = tcv({
142
142
  slots: {
143
143
  container: "flex h-32 flex-col items-center justify-center p-4 text-center",
144
- title: "text-heading-display",
144
+ title: "text-body-large-strong",
145
145
  description: "mt-2 w-32"
146
146
  },
147
147
  variants: {
@@ -179,13 +179,20 @@ var EmojiCategoryHeader = forwardRef(
179
179
  );
180
180
  }
181
181
  );
182
- function EmojiFooter({ hoveredEmoji, selectedEmoji, variant = "dark" }) {
182
+ var EmojiFooter = memo(function EmojiFooter2({
183
+ hoveredEmoji,
184
+ selectedEmoji,
185
+ variant = "dark",
186
+ i18n = {
187
+ pickAnEmoji: "Pick an emoji..."
188
+ }
189
+ }) {
183
190
  var _a, _b;
184
191
  const tv = emojiFooterTv({ variant });
185
192
  return /* @__PURE__ */ jsxs("div", { className: tv.footer(), children: [
186
193
  /* @__PURE__ */ jsx("div", { className: tv.emojiPreview(), children: ((_a = hoveredEmoji || selectedEmoji) == null ? void 0 : _a.emoji) || /* @__PURE__ */ jsx("div", { className: tv.emojiPreviewEmpty() }) }),
187
194
  /* @__PURE__ */ jsxs("div", { className: tv.emojiInfo(), children: [
188
- /* @__PURE__ */ jsx("div", { className: tv.emojiName(), children: (hoveredEmoji == null ? void 0 : hoveredEmoji.name) || (selectedEmoji == null ? void 0 : selectedEmoji.name) || "Pick an emoji..." }),
195
+ /* @__PURE__ */ jsx("div", { className: tv.emojiName(), children: (hoveredEmoji == null ? void 0 : hoveredEmoji.name) || (selectedEmoji == null ? void 0 : selectedEmoji.name) || i18n.pickAnEmoji }),
189
196
  hoveredEmoji || selectedEmoji ? /* @__PURE__ */ jsxs("div", { className: tv.emojiCode(), children: [
190
197
  ":",
191
198
  (_b = hoveredEmoji || selectedEmoji) == null ? void 0 : _b.nameUrl,
@@ -193,7 +200,7 @@ function EmojiFooter({ hoveredEmoji, selectedEmoji, variant = "dark" }) {
193
200
  ] }) : /* @__PURE__ */ jsx(Fragment, {})
194
201
  ] })
195
202
  ] });
196
- }
203
+ });
197
204
  var EmojiItem = memo(function EmojiItem2(props) {
198
205
  const { emoji, onSelect, onHover, selected = false, variant = "dark" } = props;
199
206
  return /* @__PURE__ */ jsx(
@@ -209,11 +216,17 @@ var EmojiItem = memo(function EmojiItem2(props) {
209
216
  }
210
217
  );
211
218
  });
212
- var EmojiEmpty = memo(function EmojiEmpty2({ variant = "dark" }) {
219
+ var EmojiEmpty = memo(function EmojiEmpty2({
220
+ variant = "dark",
221
+ i18n = {
222
+ title: "No emoji found",
223
+ description: "You can search for an emoji by name or use the search bar to find it."
224
+ }
225
+ }) {
213
226
  const tv = emojiEmptyTv({ variant });
214
227
  return /* @__PURE__ */ jsxs("div", { className: tv.container(), children: [
215
- /* @__PURE__ */ jsx("div", { className: tv.title(), children: "No emoji found" }),
216
- /* @__PURE__ */ jsx("p", { className: tv.description(), children: "You can search for an emoji by name or use the search bar to find it." })
228
+ /* @__PURE__ */ jsx("div", { className: tv.title(), children: i18n.title }),
229
+ /* @__PURE__ */ jsx("p", { className: tv.description(), children: i18n.description })
217
230
  ] });
218
231
  });
219
232
  var emojis = [
@@ -11727,15 +11740,15 @@ var emojis = [
11727
11740
  }
11728
11741
  ];
11729
11742
  var categories = [
11730
- { id: "frequently_used", name: "Frequently used" },
11731
- { id: "smileys_people", name: "Smileys & People", range: [1, 460] },
11732
- { id: "animals_nature", name: "Animals & Nature", range: [465, 591] },
11733
- { id: "food_drink", name: "Food & Drink", range: [592, 712] },
11734
- { id: "travel_places", name: "Travel & Places", range: [713, 922] },
11735
- { id: "activities", name: "Activities", range: [923, 1001] },
11736
- { id: "objects", name: "Objects", range: [1002, 1234] },
11737
- { id: "symbols", name: "Symbols", range: [1235, 1451] },
11738
- { id: "flags", name: "Flags", range: [1452, 1719] }
11743
+ { id: "frequently_used" },
11744
+ { id: "smileys_people", range: [1, 460] },
11745
+ { id: "animals_nature", range: [465, 591] },
11746
+ { id: "food_drink", range: [592, 712] },
11747
+ { id: "travel_places", range: [713, 922] },
11748
+ { id: "activities", range: [923, 1001] },
11749
+ { id: "objects", range: [1002, 1234] },
11750
+ { id: "symbols", range: [1235, 1451] },
11751
+ { id: "flags", range: [1452, 1719] }
11739
11752
  ];
11740
11753
  var STORAGE_KEY = "emoji-picker-frequently-used";
11741
11754
  function getEmojiCategory(id) {
@@ -11769,7 +11782,34 @@ function saveFrequentlyUsedEmoji(emojiId) {
11769
11782
  } catch {
11770
11783
  }
11771
11784
  }
11772
- function useEmojiData({ searchQuery, columns, showFrequentlyUsed }) {
11785
+ var defaultCategoryNames = {
11786
+ frequentlyUsed: "Frequently used",
11787
+ smileysPeople: "Smileys & People",
11788
+ animalsNature: "Animals & Nature",
11789
+ foodDrink: "Food & Drink",
11790
+ travelPlaces: "Travel & Places",
11791
+ activities: "Activities",
11792
+ objects: "Objects",
11793
+ symbols: "Symbols",
11794
+ flags: "Flags"
11795
+ };
11796
+ var categoryIdToI18nKey = {
11797
+ frequently_used: "frequentlyUsed",
11798
+ smileys_people: "smileysPeople",
11799
+ animals_nature: "animalsNature",
11800
+ food_drink: "foodDrink",
11801
+ travel_places: "travelPlaces",
11802
+ activities: "activities",
11803
+ objects: "objects",
11804
+ symbols: "symbols",
11805
+ flags: "flags"
11806
+ };
11807
+ function useEmojiData({
11808
+ searchQuery,
11809
+ columns,
11810
+ showFrequentlyUsed,
11811
+ categoryNames = defaultCategoryNames
11812
+ }) {
11773
11813
  const [frequentlyUsed, setFrequentlyUsed] = useState([]);
11774
11814
  useEffect(() => {
11775
11815
  if (showFrequentlyUsed) {
@@ -11799,7 +11839,7 @@ function useEmojiData({ searchQuery, columns, showFrequentlyUsed }) {
11799
11839
  items.push({
11800
11840
  type: "header",
11801
11841
  category: "frequently_used",
11802
- title: "Frequently used"
11842
+ title: categoryNames.frequentlyUsed
11803
11843
  });
11804
11844
  for (let i = 0; i < frequentlyUsed.length; i += columns) {
11805
11845
  items.push({
@@ -11814,10 +11854,11 @@ function useEmojiData({ searchQuery, columns, showFrequentlyUsed }) {
11814
11854
  (emoji) => emoji.id >= category.range[0] && emoji.id <= category.range[1]
11815
11855
  );
11816
11856
  if (categoryEmojis.length > 0) {
11857
+ const i18nKey = categoryIdToI18nKey[category.id];
11817
11858
  items.push({
11818
11859
  type: "header",
11819
11860
  category: category.id,
11820
- title: category.name
11861
+ title: i18nKey ? categoryNames[i18nKey] : category.id
11821
11862
  });
11822
11863
  for (let i = 0; i < categoryEmojis.length; i += columns) {
11823
11864
  items.push({
@@ -11828,7 +11869,7 @@ function useEmojiData({ searchQuery, columns, showFrequentlyUsed }) {
11828
11869
  }
11829
11870
  });
11830
11871
  return items;
11831
- }, [searchQuery, searchResults, frequentlyUsed, columns, showFrequentlyUsed]);
11872
+ }, [searchQuery, searchResults, frequentlyUsed, columns, showFrequentlyUsed, categoryNames]);
11832
11873
  const categoryIndexMap = useMemo(() => {
11833
11874
  const map = /* @__PURE__ */ new Map();
11834
11875
  categorizedData.forEach((item, index) => {
@@ -11884,6 +11925,8 @@ function useEmojiScroll({
11884
11925
  const [currentVisibleCategory, setCurrentVisibleCategory] = useState("frequently_used");
11885
11926
  const isScrollingToTarget = useRef(false);
11886
11927
  const isInternalUpdate = useRef(false);
11928
+ const scrollingTimeoutRef = useRef(null);
11929
+ const internalUpdateTimeoutRef = useRef(null);
11887
11930
  const virtualizer = useVirtualizer({
11888
11931
  count: categorizedData.length,
11889
11932
  getScrollElement: () => scrollRef.current,
@@ -11948,7 +11991,10 @@ function useEmojiScroll({
11948
11991
  align: "start",
11949
11992
  behavior: "auto"
11950
11993
  });
11951
- setTimeout(() => {
11994
+ if (scrollingTimeoutRef.current) {
11995
+ clearTimeout(scrollingTimeoutRef.current);
11996
+ }
11997
+ scrollingTimeoutRef.current = setTimeout(() => {
11952
11998
  isScrollingToTarget.current = false;
11953
11999
  }, 100);
11954
12000
  }
@@ -11961,17 +12007,33 @@ function useEmojiScroll({
11961
12007
  align: "center",
11962
12008
  behavior: "auto"
11963
12009
  });
11964
- setTimeout(() => {
12010
+ if (scrollingTimeoutRef.current) {
12011
+ clearTimeout(scrollingTimeoutRef.current);
12012
+ }
12013
+ scrollingTimeoutRef.current = setTimeout(() => {
11965
12014
  isScrollingToTarget.current = false;
11966
12015
  }, 100);
11967
12016
  }
11968
12017
  });
11969
12018
  const markInternalUpdate = useEventCallback(() => {
11970
12019
  isInternalUpdate.current = true;
11971
- setTimeout(() => {
12020
+ if (internalUpdateTimeoutRef.current) {
12021
+ clearTimeout(internalUpdateTimeoutRef.current);
12022
+ }
12023
+ internalUpdateTimeoutRef.current = setTimeout(() => {
11972
12024
  isInternalUpdate.current = false;
11973
12025
  }, 50);
11974
12026
  });
12027
+ useEffect(() => {
12028
+ return () => {
12029
+ if (scrollingTimeoutRef.current) {
12030
+ clearTimeout(scrollingTimeoutRef.current);
12031
+ }
12032
+ if (internalUpdateTimeoutRef.current) {
12033
+ clearTimeout(internalUpdateTimeoutRef.current);
12034
+ }
12035
+ };
12036
+ }, []);
11975
12037
  useEffect(() => {
11976
12038
  if (value && !searchQuery.trim() && !isInternalUpdate.current) {
11977
12039
  scrollToEmoji(value);
@@ -11996,17 +12058,6 @@ function useEmojiScroll({
11996
12058
  PADDING
11997
12059
  };
11998
12060
  }
11999
- var categoriesWithIcons = [
12000
- { id: "frequently_used", name: "Frequently used", icon: /* @__PURE__ */ jsx(EmojiFrequentlyUsed, {}) },
12001
- { id: "smileys_people", name: "Smileys & People", icon: /* @__PURE__ */ jsx(EmojiSmileysPeople, {}) },
12002
- { id: "animals_nature", name: "Animals & Nature", icon: /* @__PURE__ */ jsx(EmojiAnimalsNature, {}) },
12003
- { id: "food_drink", name: "Food & Drink", icon: /* @__PURE__ */ jsx(EmojiFoodDrink, {}) },
12004
- { id: "travel_places", name: "Travel & Places", icon: /* @__PURE__ */ jsx(EmojiTravelPlaces, {}) },
12005
- { id: "activities", name: "Activities", icon: /* @__PURE__ */ jsx(EmojiActivity, {}) },
12006
- { id: "objects", name: "Objects", icon: /* @__PURE__ */ jsx(EmojiObjects, {}) },
12007
- { id: "symbols", name: "Symbols", icon: /* @__PURE__ */ jsx(EmojiSymbols, {}) },
12008
- { id: "flags", name: "Flags", icon: /* @__PURE__ */ jsx(EmojiFlags, {}) }
12009
- ];
12010
12061
  var EmojiPicker = memo(function EmojiPicker2({
12011
12062
  value,
12012
12063
  onChange,
@@ -12019,11 +12070,49 @@ var EmojiPicker = memo(function EmojiPicker2({
12019
12070
  showSearch = true,
12020
12071
  showFooter = true,
12021
12072
  children,
12022
- variant = "dark"
12073
+ variant = "dark",
12074
+ i18n = {
12075
+ noEmojisFoundTitle: "No emoji found",
12076
+ noEmojisFoundDescription: "You can search for an emoji by name or use the search bar to find it.",
12077
+ footerPickAnEmoji: "Pick an emoji...",
12078
+ categories: {
12079
+ frequentlyUsed: "Frequently used",
12080
+ smileysPeople: "Smileys & People",
12081
+ animalsNature: "Animals & Nature",
12082
+ foodDrink: "Food & Drink",
12083
+ travelPlaces: "Travel & Places",
12084
+ activities: "Activities",
12085
+ objects: "Objects",
12086
+ symbols: "Symbols",
12087
+ flags: "Flags"
12088
+ }
12089
+ }
12023
12090
  }) {
12024
12091
  const [searchQuery, setSearchQuery] = useState("");
12025
12092
  const [hoveredEmoji, setHoveredEmoji] = useState(null);
12093
+ const safeColumns = Math.max(1, columns);
12026
12094
  const tv = emojiTv({ variant });
12095
+ const categoriesWithIcons = useMemo(
12096
+ () => {
12097
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
12098
+ return [
12099
+ {
12100
+ id: "frequently_used",
12101
+ name: (_a = i18n.categories) == null ? void 0 : _a.frequentlyUsed,
12102
+ icon: /* @__PURE__ */ jsx(EmojiFrequentlyUsed, {})
12103
+ },
12104
+ { id: "smileys_people", name: (_b = i18n.categories) == null ? void 0 : _b.smileysPeople, icon: /* @__PURE__ */ jsx(EmojiSmileysPeople, {}) },
12105
+ { id: "animals_nature", name: (_c = i18n.categories) == null ? void 0 : _c.animalsNature, icon: /* @__PURE__ */ jsx(EmojiAnimalsNature, {}) },
12106
+ { id: "food_drink", name: (_d = i18n.categories) == null ? void 0 : _d.foodDrink, icon: /* @__PURE__ */ jsx(EmojiFoodDrink, {}) },
12107
+ { id: "travel_places", name: (_e = i18n.categories) == null ? void 0 : _e.travelPlaces, icon: /* @__PURE__ */ jsx(EmojiTravelPlaces, {}) },
12108
+ { id: "activities", name: (_f = i18n.categories) == null ? void 0 : _f.activities, icon: /* @__PURE__ */ jsx(EmojiActivity, {}) },
12109
+ { id: "objects", name: (_g = i18n.categories) == null ? void 0 : _g.objects, icon: /* @__PURE__ */ jsx(EmojiObjects, {}) },
12110
+ { id: "symbols", name: (_h = i18n.categories) == null ? void 0 : _h.symbols, icon: /* @__PURE__ */ jsx(EmojiSymbols, {}) },
12111
+ { id: "flags", name: (_i = i18n.categories) == null ? void 0 : _i.flags, icon: /* @__PURE__ */ jsx(EmojiFlags, {}) }
12112
+ ];
12113
+ },
12114
+ [i18n.categories]
12115
+ );
12027
12116
  const {
12028
12117
  categorizedData,
12029
12118
  categoryIndexMap,
@@ -12031,8 +12120,9 @@ var EmojiPicker = memo(function EmojiPicker2({
12031
12120
  findEmojiPosition
12032
12121
  } = useEmojiData({
12033
12122
  searchQuery,
12034
- columns,
12035
- showFrequentlyUsed
12123
+ columns: safeColumns,
12124
+ showFrequentlyUsed,
12125
+ categoryNames: i18n.categories
12036
12126
  });
12037
12127
  const {
12038
12128
  scrollRef,
@@ -12048,7 +12138,7 @@ var EmojiPicker = memo(function EmojiPicker2({
12048
12138
  findEmojiPosition,
12049
12139
  searchQuery,
12050
12140
  value,
12051
- columns
12141
+ columns: safeColumns
12052
12142
  });
12053
12143
  const availableCategories = useMemo(() => {
12054
12144
  return categoriesWithIcons.filter((category) => {
@@ -12057,7 +12147,7 @@ var EmojiPicker = memo(function EmojiPicker2({
12057
12147
  }
12058
12148
  return true;
12059
12149
  });
12060
- }, [showFrequentlyUsed]);
12150
+ }, [categoriesWithIcons, showFrequentlyUsed]);
12061
12151
  const handleEmojiSelect = useEventCallback((emoji) => {
12062
12152
  markInternalUpdate();
12063
12153
  addToFrequentlyUsed(emoji.id);
@@ -12073,7 +12163,7 @@ var EmojiPicker = memo(function EmojiPicker2({
12073
12163
  const rootStyle = {
12074
12164
  "--emoji-height": `${height}px`,
12075
12165
  "--emoji-padding": `${PADDING2}px`,
12076
- "--emoji-columns": `${columns}`
12166
+ "--emoji-columns": `${safeColumns}`
12077
12167
  };
12078
12168
  return /* @__PURE__ */ jsxs(
12079
12169
  "div",
@@ -12176,7 +12266,16 @@ var EmojiPicker = memo(function EmojiPicker2({
12176
12266
  },
12177
12267
  virtualItem.key
12178
12268
  );
12179
- }) : /* @__PURE__ */ jsx(EmojiEmpty, { variant })
12269
+ }) : /* @__PURE__ */ jsx(
12270
+ EmojiEmpty,
12271
+ {
12272
+ variant,
12273
+ i18n: {
12274
+ title: i18n.noEmojisFoundTitle,
12275
+ description: i18n.noEmojisFoundDescription
12276
+ }
12277
+ }
12278
+ )
12180
12279
  }
12181
12280
  )
12182
12281
  }
@@ -12188,7 +12287,10 @@ var EmojiPicker = memo(function EmojiPicker2({
12188
12287
  {
12189
12288
  hoveredEmoji,
12190
12289
  selectedEmoji: value || null,
12191
- variant
12290
+ variant,
12291
+ i18n: {
12292
+ pickAnEmoji: i18n.footerPickAnEmoji
12293
+ }
12192
12294
  }
12193
12295
  ),
12194
12296
  children
@@ -1,5 +1,9 @@
1
1
  interface EmojiEmptyProps {
2
2
  variant?: "default" | "dark" | "light";
3
+ i18n?: {
4
+ title?: string;
5
+ description?: string;
6
+ };
3
7
  }
4
8
  export declare const EmojiEmpty: import('react').NamedExoticComponent<EmojiEmptyProps>;
5
9
  export {};
@@ -1,11 +1,17 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import { memo } from "react";
3
3
  import { emojiEmptyTv } from "../tv.js";
4
- const EmojiEmpty = memo(function EmojiEmpty2({ variant = "dark" }) {
4
+ const EmojiEmpty = memo(function EmojiEmpty2({
5
+ variant = "dark",
6
+ i18n = {
7
+ title: "No emoji found",
8
+ description: "You can search for an emoji by name or use the search bar to find it."
9
+ }
10
+ }) {
5
11
  const tv = emojiEmptyTv({ variant });
6
12
  return /* @__PURE__ */ jsxs("div", { className: tv.container(), children: [
7
- /* @__PURE__ */ jsx("div", { className: tv.title(), children: "No emoji found" }),
8
- /* @__PURE__ */ jsx("p", { className: tv.description(), children: "You can search for an emoji by name or use the search bar to find it." })
13
+ /* @__PURE__ */ jsx("div", { className: tv.title(), children: i18n.title }),
14
+ /* @__PURE__ */ jsx("p", { className: tv.description(), children: i18n.description })
9
15
  ] });
10
16
  });
11
17
  export {
@@ -3,6 +3,9 @@ interface EmojiFooterProps {
3
3
  hoveredEmoji: EmojiData | null;
4
4
  selectedEmoji: EmojiData | null;
5
5
  variant?: "default" | "dark" | "light";
6
+ i18n?: {
7
+ pickAnEmoji?: string;
8
+ };
6
9
  }
7
- export declare function EmojiFooter({ hoveredEmoji, selectedEmoji, variant }: EmojiFooterProps): import("react/jsx-runtime").JSX.Element;
10
+ export declare const EmojiFooter: import('react').NamedExoticComponent<EmojiFooterProps>;
8
11
  export {};
@@ -1,12 +1,20 @@
1
1
  import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
+ import { memo } from "react";
2
3
  import { emojiFooterTv } from "../tv.js";
3
- function EmojiFooter({ hoveredEmoji, selectedEmoji, variant = "dark" }) {
4
+ const EmojiFooter = memo(function EmojiFooter2({
5
+ hoveredEmoji,
6
+ selectedEmoji,
7
+ variant = "dark",
8
+ i18n = {
9
+ pickAnEmoji: "Pick an emoji..."
10
+ }
11
+ }) {
4
12
  var _a, _b;
5
13
  const tv = emojiFooterTv({ variant });
6
14
  return /* @__PURE__ */ jsxs("div", { className: tv.footer(), children: [
7
15
  /* @__PURE__ */ jsx("div", { className: tv.emojiPreview(), children: ((_a = hoveredEmoji || selectedEmoji) == null ? void 0 : _a.emoji) || /* @__PURE__ */ jsx("div", { className: tv.emojiPreviewEmpty() }) }),
8
16
  /* @__PURE__ */ jsxs("div", { className: tv.emojiInfo(), children: [
9
- /* @__PURE__ */ jsx("div", { className: tv.emojiName(), children: (hoveredEmoji == null ? void 0 : hoveredEmoji.name) || (selectedEmoji == null ? void 0 : selectedEmoji.name) || "Pick an emoji..." }),
17
+ /* @__PURE__ */ jsx("div", { className: tv.emojiName(), children: (hoveredEmoji == null ? void 0 : hoveredEmoji.name) || (selectedEmoji == null ? void 0 : selectedEmoji.name) || i18n.pickAnEmoji }),
10
18
  hoveredEmoji || selectedEmoji ? /* @__PURE__ */ jsxs("div", { className: tv.emojiCode(), children: [
11
19
  ":",
12
20
  (_b = hoveredEmoji || selectedEmoji) == null ? void 0 : _b.nameUrl,
@@ -14,7 +22,7 @@ function EmojiFooter({ hoveredEmoji, selectedEmoji, variant = "dark" }) {
14
22
  ] }) : /* @__PURE__ */ jsx(Fragment, {})
15
23
  ] })
16
24
  ] });
17
- }
25
+ });
18
26
  export {
19
27
  EmojiFooter
20
28
  };
@@ -13,6 +13,22 @@ export interface EmojiPickerProps {
13
13
  showFooter?: boolean;
14
14
  value?: EmojiData | null;
15
15
  variant?: "default" | "dark" | "light";
16
+ i18n?: {
17
+ noEmojisFoundTitle?: string;
18
+ noEmojisFoundDescription?: string;
19
+ footerPickAnEmoji?: string;
20
+ categories?: {
21
+ frequentlyUsed: string;
22
+ smileysPeople: string;
23
+ animalsNature: string;
24
+ foodDrink: string;
25
+ travelPlaces: string;
26
+ activities: string;
27
+ objects: string;
28
+ symbols: string;
29
+ flags: string;
30
+ };
31
+ };
16
32
  }
17
33
  export declare const EmojiPicker: React.NamedExoticComponent<EmojiPickerProps>;
18
34
  export default EmojiPicker;
@@ -12,17 +12,6 @@ import { EmojiCategoryHeader } from "./components/emoji-category-header.js";
12
12
  import { EmojiItem } from "./components/emoji-item.js";
13
13
  import { EmojiEmpty } from "./components/emoji-empty.js";
14
14
  import { EmojiFooter } from "./components/emoji-footer.js";
15
- const categoriesWithIcons = [
16
- { id: "frequently_used", name: "Frequently used", icon: /* @__PURE__ */ jsx(EmojiFrequentlyUsed, {}) },
17
- { id: "smileys_people", name: "Smileys & People", icon: /* @__PURE__ */ jsx(EmojiSmileysPeople, {}) },
18
- { id: "animals_nature", name: "Animals & Nature", icon: /* @__PURE__ */ jsx(EmojiAnimalsNature, {}) },
19
- { id: "food_drink", name: "Food & Drink", icon: /* @__PURE__ */ jsx(EmojiFoodDrink, {}) },
20
- { id: "travel_places", name: "Travel & Places", icon: /* @__PURE__ */ jsx(EmojiTravelPlaces, {}) },
21
- { id: "activities", name: "Activities", icon: /* @__PURE__ */ jsx(EmojiActivity, {}) },
22
- { id: "objects", name: "Objects", icon: /* @__PURE__ */ jsx(EmojiObjects, {}) },
23
- { id: "symbols", name: "Symbols", icon: /* @__PURE__ */ jsx(EmojiSymbols, {}) },
24
- { id: "flags", name: "Flags", icon: /* @__PURE__ */ jsx(EmojiFlags, {}) }
25
- ];
26
15
  const EmojiPicker = memo(function EmojiPicker2({
27
16
  value,
28
17
  onChange,
@@ -35,11 +24,49 @@ const EmojiPicker = memo(function EmojiPicker2({
35
24
  showSearch = true,
36
25
  showFooter = true,
37
26
  children,
38
- variant = "dark"
27
+ variant = "dark",
28
+ i18n = {
29
+ noEmojisFoundTitle: "No emoji found",
30
+ noEmojisFoundDescription: "You can search for an emoji by name or use the search bar to find it.",
31
+ footerPickAnEmoji: "Pick an emoji...",
32
+ categories: {
33
+ frequentlyUsed: "Frequently used",
34
+ smileysPeople: "Smileys & People",
35
+ animalsNature: "Animals & Nature",
36
+ foodDrink: "Food & Drink",
37
+ travelPlaces: "Travel & Places",
38
+ activities: "Activities",
39
+ objects: "Objects",
40
+ symbols: "Symbols",
41
+ flags: "Flags"
42
+ }
43
+ }
39
44
  }) {
40
45
  const [searchQuery, setSearchQuery] = useState("");
41
46
  const [hoveredEmoji, setHoveredEmoji] = useState(null);
47
+ const safeColumns = Math.max(1, columns);
42
48
  const tv = emojiTv({ variant });
49
+ const categoriesWithIcons = useMemo(
50
+ () => {
51
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i;
52
+ return [
53
+ {
54
+ id: "frequently_used",
55
+ name: (_a = i18n.categories) == null ? void 0 : _a.frequentlyUsed,
56
+ icon: /* @__PURE__ */ jsx(EmojiFrequentlyUsed, {})
57
+ },
58
+ { id: "smileys_people", name: (_b = i18n.categories) == null ? void 0 : _b.smileysPeople, icon: /* @__PURE__ */ jsx(EmojiSmileysPeople, {}) },
59
+ { id: "animals_nature", name: (_c = i18n.categories) == null ? void 0 : _c.animalsNature, icon: /* @__PURE__ */ jsx(EmojiAnimalsNature, {}) },
60
+ { id: "food_drink", name: (_d = i18n.categories) == null ? void 0 : _d.foodDrink, icon: /* @__PURE__ */ jsx(EmojiFoodDrink, {}) },
61
+ { id: "travel_places", name: (_e = i18n.categories) == null ? void 0 : _e.travelPlaces, icon: /* @__PURE__ */ jsx(EmojiTravelPlaces, {}) },
62
+ { id: "activities", name: (_f = i18n.categories) == null ? void 0 : _f.activities, icon: /* @__PURE__ */ jsx(EmojiActivity, {}) },
63
+ { id: "objects", name: (_g = i18n.categories) == null ? void 0 : _g.objects, icon: /* @__PURE__ */ jsx(EmojiObjects, {}) },
64
+ { id: "symbols", name: (_h = i18n.categories) == null ? void 0 : _h.symbols, icon: /* @__PURE__ */ jsx(EmojiSymbols, {}) },
65
+ { id: "flags", name: (_i = i18n.categories) == null ? void 0 : _i.flags, icon: /* @__PURE__ */ jsx(EmojiFlags, {}) }
66
+ ];
67
+ },
68
+ [i18n.categories]
69
+ );
43
70
  const {
44
71
  categorizedData,
45
72
  categoryIndexMap,
@@ -48,8 +75,9 @@ const EmojiPicker = memo(function EmojiPicker2({
48
75
  findEmojiByChar
49
76
  } = useEmojiData({
50
77
  searchQuery,
51
- columns,
52
- showFrequentlyUsed
78
+ columns: safeColumns,
79
+ showFrequentlyUsed,
80
+ categoryNames: i18n.categories
53
81
  });
54
82
  const {
55
83
  scrollRef,
@@ -65,7 +93,7 @@ const EmojiPicker = memo(function EmojiPicker2({
65
93
  findEmojiPosition,
66
94
  searchQuery,
67
95
  value,
68
- columns
96
+ columns: safeColumns
69
97
  });
70
98
  const availableCategories = useMemo(() => {
71
99
  return categoriesWithIcons.filter((category) => {
@@ -74,7 +102,7 @@ const EmojiPicker = memo(function EmojiPicker2({
74
102
  }
75
103
  return true;
76
104
  });
77
- }, [showFrequentlyUsed]);
105
+ }, [categoriesWithIcons, showFrequentlyUsed]);
78
106
  const handleEmojiSelect = useEventCallback((emoji) => {
79
107
  markInternalUpdate();
80
108
  addToFrequentlyUsed(emoji.id);
@@ -90,7 +118,7 @@ const EmojiPicker = memo(function EmojiPicker2({
90
118
  const rootStyle = {
91
119
  "--emoji-height": `${height}px`,
92
120
  "--emoji-padding": `${PADDING}px`,
93
- "--emoji-columns": `${columns}`
121
+ "--emoji-columns": `${safeColumns}`
94
122
  };
95
123
  return /* @__PURE__ */ jsxs(
96
124
  "div",
@@ -193,7 +221,16 @@ const EmojiPicker = memo(function EmojiPicker2({
193
221
  },
194
222
  virtualItem.key
195
223
  );
196
- }) : /* @__PURE__ */ jsx(EmojiEmpty, { variant })
224
+ }) : /* @__PURE__ */ jsx(
225
+ EmojiEmpty,
226
+ {
227
+ variant,
228
+ i18n: {
229
+ title: i18n.noEmojisFoundTitle,
230
+ description: i18n.noEmojisFoundDescription
231
+ }
232
+ }
233
+ )
197
234
  }
198
235
  )
199
236
  }
@@ -205,7 +242,10 @@ const EmojiPicker = memo(function EmojiPicker2({
205
242
  {
206
243
  hoveredEmoji,
207
244
  selectedEmoji: value || null,
208
- variant
245
+ variant,
246
+ i18n: {
247
+ pickAnEmoji: i18n.footerPickAnEmoji
248
+ }
209
249
  }
210
250
  ),
211
251
  children
@@ -1,3 +1,3 @@
1
1
  export { useEmojiData } from './use-emoji-data';
2
2
  export { useEmojiScroll } from './use-emoji-scroll';
3
- export type { EmojiData, EmojiCategory, VirtualItem } from './use-emoji-data';
3
+ export type { EmojiData, EmojiCategory, VirtualItem, CategoryNames } from './use-emoji-data';
@@ -16,47 +16,50 @@ export type VirtualItem = {
16
16
  };
17
17
  export declare const categories: readonly [{
18
18
  readonly id: "frequently_used";
19
- readonly name: "Frequently used";
20
19
  }, {
21
20
  readonly id: "smileys_people";
22
- readonly name: "Smileys & People";
23
21
  readonly range: readonly [1, 460];
24
22
  }, {
25
23
  readonly id: "animals_nature";
26
- readonly name: "Animals & Nature";
27
24
  readonly range: readonly [465, 591];
28
25
  }, {
29
26
  readonly id: "food_drink";
30
- readonly name: "Food & Drink";
31
27
  readonly range: readonly [592, 712];
32
28
  }, {
33
29
  readonly id: "travel_places";
34
- readonly name: "Travel & Places";
35
30
  readonly range: readonly [713, 922];
36
31
  }, {
37
32
  readonly id: "activities";
38
- readonly name: "Activities";
39
33
  readonly range: readonly [923, 1001];
40
34
  }, {
41
35
  readonly id: "objects";
42
- readonly name: "Objects";
43
36
  readonly range: readonly [1002, 1234];
44
37
  }, {
45
38
  readonly id: "symbols";
46
- readonly name: "Symbols";
47
39
  readonly range: readonly [1235, 1451];
48
40
  }, {
49
41
  readonly id: "flags";
50
- readonly name: "Flags";
51
42
  readonly range: readonly [1452, 1719];
52
43
  }];
53
44
  export declare function getEmojiCategory(id: number): EmojiCategory;
45
+ export interface CategoryNames {
46
+ activities: string;
47
+ animalsNature: string;
48
+ flags: string;
49
+ foodDrink: string;
50
+ frequentlyUsed: string;
51
+ objects: string;
52
+ smileysPeople: string;
53
+ symbols: string;
54
+ travelPlaces: string;
55
+ }
54
56
  interface UseEmojiDataProps {
57
+ categoryNames?: CategoryNames;
55
58
  columns: number;
56
59
  searchQuery: string;
57
60
  showFrequentlyUsed: boolean;
58
61
  }
59
- export declare function useEmojiData({ searchQuery, columns, showFrequentlyUsed }: UseEmojiDataProps): {
62
+ export declare function useEmojiData({ searchQuery, columns, showFrequentlyUsed, categoryNames, }: UseEmojiDataProps): {
60
63
  categorizedData: VirtualItem[];
61
64
  categoryIndexMap: Map<EmojiCategory, number>;
62
65
  searchResults: {
@@ -2,15 +2,15 @@ import { useState, useEffect, useMemo } from "react";
2
2
  import { emojis } from "../utils/emoji-data.js";
3
3
  import { isBrowser } from "../../../../shared/utils/ssr.js";
4
4
  const categories = [
5
- { id: "frequently_used", name: "Frequently used" },
6
- { id: "smileys_people", name: "Smileys & People", range: [1, 460] },
7
- { id: "animals_nature", name: "Animals & Nature", range: [465, 591] },
8
- { id: "food_drink", name: "Food & Drink", range: [592, 712] },
9
- { id: "travel_places", name: "Travel & Places", range: [713, 922] },
10
- { id: "activities", name: "Activities", range: [923, 1001] },
11
- { id: "objects", name: "Objects", range: [1002, 1234] },
12
- { id: "symbols", name: "Symbols", range: [1235, 1451] },
13
- { id: "flags", name: "Flags", range: [1452, 1719] }
5
+ { id: "frequently_used" },
6
+ { id: "smileys_people", range: [1, 460] },
7
+ { id: "animals_nature", range: [465, 591] },
8
+ { id: "food_drink", range: [592, 712] },
9
+ { id: "travel_places", range: [713, 922] },
10
+ { id: "activities", range: [923, 1001] },
11
+ { id: "objects", range: [1002, 1234] },
12
+ { id: "symbols", range: [1235, 1451] },
13
+ { id: "flags", range: [1452, 1719] }
14
14
  ];
15
15
  const STORAGE_KEY = "emoji-picker-frequently-used";
16
16
  function getEmojiCategory(id) {
@@ -44,7 +44,34 @@ function saveFrequentlyUsedEmoji(emojiId) {
44
44
  } catch {
45
45
  }
46
46
  }
47
- function useEmojiData({ searchQuery, columns, showFrequentlyUsed }) {
47
+ const defaultCategoryNames = {
48
+ frequentlyUsed: "Frequently used",
49
+ smileysPeople: "Smileys & People",
50
+ animalsNature: "Animals & Nature",
51
+ foodDrink: "Food & Drink",
52
+ travelPlaces: "Travel & Places",
53
+ activities: "Activities",
54
+ objects: "Objects",
55
+ symbols: "Symbols",
56
+ flags: "Flags"
57
+ };
58
+ const categoryIdToI18nKey = {
59
+ frequently_used: "frequentlyUsed",
60
+ smileys_people: "smileysPeople",
61
+ animals_nature: "animalsNature",
62
+ food_drink: "foodDrink",
63
+ travel_places: "travelPlaces",
64
+ activities: "activities",
65
+ objects: "objects",
66
+ symbols: "symbols",
67
+ flags: "flags"
68
+ };
69
+ function useEmojiData({
70
+ searchQuery,
71
+ columns,
72
+ showFrequentlyUsed,
73
+ categoryNames = defaultCategoryNames
74
+ }) {
48
75
  const [frequentlyUsed, setFrequentlyUsed] = useState([]);
49
76
  useEffect(() => {
50
77
  if (showFrequentlyUsed) {
@@ -74,7 +101,7 @@ function useEmojiData({ searchQuery, columns, showFrequentlyUsed }) {
74
101
  items.push({
75
102
  type: "header",
76
103
  category: "frequently_used",
77
- title: "Frequently used"
104
+ title: categoryNames.frequentlyUsed
78
105
  });
79
106
  for (let i = 0; i < frequentlyUsed.length; i += columns) {
80
107
  items.push({
@@ -89,10 +116,11 @@ function useEmojiData({ searchQuery, columns, showFrequentlyUsed }) {
89
116
  (emoji) => emoji.id >= category.range[0] && emoji.id <= category.range[1]
90
117
  );
91
118
  if (categoryEmojis.length > 0) {
119
+ const i18nKey = categoryIdToI18nKey[category.id];
92
120
  items.push({
93
121
  type: "header",
94
122
  category: category.id,
95
- title: category.name
123
+ title: i18nKey ? categoryNames[i18nKey] : category.id
96
124
  });
97
125
  for (let i = 0; i < categoryEmojis.length; i += columns) {
98
126
  items.push({
@@ -103,7 +131,7 @@ function useEmojiData({ searchQuery, columns, showFrequentlyUsed }) {
103
131
  }
104
132
  });
105
133
  return items;
106
- }, [searchQuery, searchResults, frequentlyUsed, columns, showFrequentlyUsed]);
134
+ }, [searchQuery, searchResults, frequentlyUsed, columns, showFrequentlyUsed, categoryNames]);
107
135
  const categoryIndexMap = useMemo(() => {
108
136
  const map = /* @__PURE__ */ new Map();
109
137
  categorizedData.forEach((item, index) => {
@@ -17,6 +17,8 @@ function useEmojiScroll({
17
17
  const [currentVisibleCategory, setCurrentVisibleCategory] = useState("frequently_used");
18
18
  const isScrollingToTarget = useRef(false);
19
19
  const isInternalUpdate = useRef(false);
20
+ const scrollingTimeoutRef = useRef(null);
21
+ const internalUpdateTimeoutRef = useRef(null);
20
22
  const virtualizer = useVirtualizer({
21
23
  count: categorizedData.length,
22
24
  getScrollElement: () => scrollRef.current,
@@ -81,7 +83,10 @@ function useEmojiScroll({
81
83
  align: "start",
82
84
  behavior: "auto"
83
85
  });
84
- setTimeout(() => {
86
+ if (scrollingTimeoutRef.current) {
87
+ clearTimeout(scrollingTimeoutRef.current);
88
+ }
89
+ scrollingTimeoutRef.current = setTimeout(() => {
85
90
  isScrollingToTarget.current = false;
86
91
  }, 100);
87
92
  }
@@ -94,17 +99,33 @@ function useEmojiScroll({
94
99
  align: "center",
95
100
  behavior: "auto"
96
101
  });
97
- setTimeout(() => {
102
+ if (scrollingTimeoutRef.current) {
103
+ clearTimeout(scrollingTimeoutRef.current);
104
+ }
105
+ scrollingTimeoutRef.current = setTimeout(() => {
98
106
  isScrollingToTarget.current = false;
99
107
  }, 100);
100
108
  }
101
109
  });
102
110
  const markInternalUpdate = useEventCallback(() => {
103
111
  isInternalUpdate.current = true;
104
- setTimeout(() => {
112
+ if (internalUpdateTimeoutRef.current) {
113
+ clearTimeout(internalUpdateTimeoutRef.current);
114
+ }
115
+ internalUpdateTimeoutRef.current = setTimeout(() => {
105
116
  isInternalUpdate.current = false;
106
117
  }, 50);
107
118
  });
119
+ useEffect(() => {
120
+ return () => {
121
+ if (scrollingTimeoutRef.current) {
122
+ clearTimeout(scrollingTimeoutRef.current);
123
+ }
124
+ if (internalUpdateTimeoutRef.current) {
125
+ clearTimeout(internalUpdateTimeoutRef.current);
126
+ }
127
+ };
128
+ }, []);
108
129
  useEffect(() => {
109
130
  if (value && !searchQuery.trim() && !isInternalUpdate.current) {
110
131
  scrollToEmoji(value);
@@ -132,7 +132,7 @@ const emojiFooterTv = tcv({
132
132
  const emojiEmptyTv = tcv({
133
133
  slots: {
134
134
  container: "flex h-32 flex-col items-center justify-center p-4 text-center",
135
- title: "text-heading-display",
135
+ title: "text-body-large-strong",
136
136
  description: "mt-2 w-32"
137
137
  },
138
138
  variants: {
@@ -14,7 +14,7 @@ interface IconButtonProps extends Omit<HTMLProps<HTMLElement>, "size" | "as"> {
14
14
  readOnly?: boolean;
15
15
  size?: "default" | "large" | "reset";
16
16
  tooltip?: TooltipProps;
17
- variant?: "default" | "secondary" | "solid" | "highlight" | "ghost" | "dark" | "reset";
17
+ variant?: "default" | "secondary" | "solid" | "highlight" | "ghost" | "dark" | "submit" | "reset";
18
18
  }
19
19
  declare const IconButton: react.ForwardRefExoticComponent<Omit<IconButtonProps, "ref"> & react.RefAttributes<HTMLElement>>;
20
20
 
@@ -28,6 +28,12 @@ var iconButtonTv = tcv({
28
28
  highlight: {},
29
29
  ghost: {},
30
30
  dark: { button: "text-white" },
31
+ submit: {
32
+ button: [
33
+ "bg-[var(--theme-submit-btn-bg)] text-[var(--theme-submit-btn-text)]",
34
+ "focus-visible:border-transparent"
35
+ ]
36
+ },
31
37
  reset: {}
32
38
  },
33
39
  active: {
@@ -141,6 +147,29 @@ var iconButtonTv = tcv({
141
147
  variant: "dark",
142
148
  disabled: true,
143
149
  class: { button: "text-gray-500" }
150
+ },
151
+ {
152
+ variant: "submit",
153
+ class: {
154
+ button: "focus-visible:shadow-focus"
155
+ }
156
+ },
157
+ {
158
+ variant: "submit",
159
+ focused: true,
160
+ class: {
161
+ button: ["shadow-focus", "border-transparent"]
162
+ }
163
+ },
164
+ {
165
+ variant: "submit",
166
+ loading: true,
167
+ class: { button: "opacity-70" }
168
+ },
169
+ {
170
+ variant: "submit",
171
+ disabled: true,
172
+ class: { button: "opacity-50" }
144
173
  }
145
174
  ],
146
175
  defaultVariants: {
@@ -507,7 +507,9 @@ var TextareaTv = tcv({
507
507
  container: "bg-gray-700",
508
508
  textarea: "text-white placeholder:text-white/40"
509
509
  },
510
- reset: {}
510
+ reset: {
511
+ container: "rounded-none border-0"
512
+ }
511
513
  },
512
514
  resize: {
513
515
  auto: {},
@@ -10,6 +10,7 @@ interface TooltipProps {
10
10
  className?: string;
11
11
  content?: React.ReactNode;
12
12
  disabled?: boolean;
13
+ interactive?: boolean;
13
14
  offset?: number;
14
15
  onOpenChange?: (open: boolean) => void;
15
16
  open?: boolean;
@@ -23,6 +24,7 @@ interface TooltipProps {
23
24
  withArrow?: boolean;
24
25
  }
25
26
  interface TooltipContentProps extends React.HTMLProps<HTMLDivElement> {
27
+ interactive?: boolean;
26
28
  portalId?: string;
27
29
  variant?: "default" | "light";
28
30
  withArrow?: boolean;
@@ -128,7 +128,15 @@ var TooltipArrow = forwardRef(function TooltipArrow2({ className, variant = "def
128
128
  });
129
129
  var TooltipContent = forwardRef(
130
130
  function TooltipContent2(props, propRef) {
131
- const { className, withArrow = true, variant = "default", children, portalId, ...rest } = props;
131
+ const {
132
+ className,
133
+ withArrow = true,
134
+ variant = "default",
135
+ children,
136
+ portalId,
137
+ interactive = true,
138
+ ...rest
139
+ } = props;
132
140
  const state = useTooltipState();
133
141
  const ref = useMergeRefs([state.refs.setFloating, propRef]);
134
142
  const { isInstantPhase, currentId } = useDelayGroup(state.context, {
@@ -157,19 +165,27 @@ var TooltipContent = forwardRef(
157
165
  });
158
166
  const tv = useMemo(() => tooltipContentVariants({ variant }), [variant]);
159
167
  if (state.disabled || !isMounted) return null;
168
+ const floatingProps = state.getFloatingProps(rest);
160
169
  return /* @__PURE__ */ jsx(FloatingPortal, { id: portalId, children: /* @__PURE__ */ jsx(
161
170
  "div",
162
171
  {
163
172
  ref,
164
- style: state.floatingStyles,
165
- ...state.getFloatingProps(rest),
173
+ style: {
174
+ ...state.floatingStyles,
175
+ pointerEvents: interactive ? void 0 : "none"
176
+ },
177
+ ...floatingProps,
166
178
  className: "z-tooltip",
167
179
  children: /* @__PURE__ */ jsxs(
168
180
  "div",
169
181
  {
170
182
  className: tcx(tv.root({ className })),
171
183
  "data-state": state.open ? "open" : "closed",
172
- style: styles,
184
+ style: {
185
+ ...styles,
186
+ pointerEvents: interactive ? void 0 : "none",
187
+ cursor: interactive ? void 0 : "default"
188
+ },
173
189
  children: [
174
190
  children,
175
191
  withArrow && /* @__PURE__ */ jsx(TooltipArrow, { variant })
@@ -272,7 +288,8 @@ function TooltipRoot(props) {
272
288
  withArrow = true,
273
289
  variant = "default",
274
290
  offset: offset2 = 8,
275
- portalId = PORTAL_ROOT_ID
291
+ portalId = PORTAL_ROOT_ID,
292
+ interactive = true
276
293
  } = props;
277
294
  const tooltip = useTooltip({
278
295
  placement,
@@ -291,6 +308,7 @@ function TooltipRoot(props) {
291
308
  variant,
292
309
  portalId,
293
310
  className,
311
+ interactive,
294
312
  children: [
295
313
  content,
296
314
  shortcut && /* @__PURE__ */ jsx(
@@ -7,7 +7,15 @@ import { TooltipArrow } from "./tooltip-arrow.js";
7
7
  import { tcx } from "../../../../shared/utils/tcx/tcx.js";
8
8
  const TooltipContent = forwardRef(
9
9
  function TooltipContent2(props, propRef) {
10
- const { className, withArrow = true, variant = "default", children, portalId, ...rest } = props;
10
+ const {
11
+ className,
12
+ withArrow = true,
13
+ variant = "default",
14
+ children,
15
+ portalId,
16
+ interactive = true,
17
+ ...rest
18
+ } = props;
11
19
  const state = useTooltipState();
12
20
  const ref = useMergeRefs([state.refs.setFloating, propRef]);
13
21
  const { isInstantPhase, currentId } = useDelayGroup(state.context, {
@@ -36,19 +44,27 @@ const TooltipContent = forwardRef(
36
44
  });
37
45
  const tv = useMemo(() => tooltipContentVariants({ variant }), [variant]);
38
46
  if (state.disabled || !isMounted) return null;
47
+ const floatingProps = state.getFloatingProps(rest);
39
48
  return /* @__PURE__ */ jsx(FloatingPortal, { id: portalId, children: /* @__PURE__ */ jsx(
40
49
  "div",
41
50
  {
42
51
  ref,
43
- style: state.floatingStyles,
44
- ...state.getFloatingProps(rest),
52
+ style: {
53
+ ...state.floatingStyles,
54
+ pointerEvents: interactive ? void 0 : "none"
55
+ },
56
+ ...floatingProps,
45
57
  className: "z-tooltip",
46
58
  children: /* @__PURE__ */ jsxs(
47
59
  "div",
48
60
  {
49
61
  className: tcx(tv.root({ className })),
50
62
  "data-state": state.open ? "open" : "closed",
51
- style: styles,
63
+ style: {
64
+ ...styles,
65
+ pointerEvents: interactive ? void 0 : "none",
66
+ cursor: interactive ? void 0 : "default"
67
+ },
52
68
  children: [
53
69
  children,
54
70
  withArrow && /* @__PURE__ */ jsx(TooltipArrow, { variant })
@@ -17,7 +17,8 @@ function TooltipRoot(props) {
17
17
  withArrow = true,
18
18
  variant = "default",
19
19
  offset = 8,
20
- portalId = PORTAL_ROOT_ID
20
+ portalId = PORTAL_ROOT_ID,
21
+ interactive = true
21
22
  } = props;
22
23
  const tooltip = useTooltip({
23
24
  placement,
@@ -36,6 +37,7 @@ function TooltipRoot(props) {
36
37
  variant,
37
38
  portalId,
38
39
  className,
40
+ interactive,
39
41
  children: [
40
42
  content,
41
43
  shortcut && /* @__PURE__ */ jsx(
@@ -4,6 +4,7 @@ import { KbdKey } from '../../kbd/src';
4
4
  export interface TooltipOptions {
5
5
  disabled?: boolean;
6
6
  initialOpen?: boolean;
7
+ interactive?: boolean;
7
8
  offset?: number;
8
9
  onOpenChange?: (open: boolean) => void;
9
10
  open?: boolean;
@@ -32,6 +33,7 @@ export interface TooltipProps {
32
33
  className?: string;
33
34
  content?: React.ReactNode;
34
35
  disabled?: boolean;
36
+ interactive?: boolean;
35
37
  offset?: number;
36
38
  onOpenChange?: (open: boolean) => void;
37
39
  open?: boolean;
@@ -45,6 +47,7 @@ export interface TooltipProps {
45
47
  withArrow?: boolean;
46
48
  }
47
49
  export interface TooltipContentProps extends React.HTMLProps<HTMLDivElement> {
50
+ interactive?: boolean;
48
51
  portalId?: string;
49
52
  variant?: "default" | "light";
50
53
  withArrow?: boolean;
@@ -0,0 +1,48 @@
1
+ import { MenuContextContent, MenuDivider, MenuEmpty, MenuContextItem, MenuSearch, MenuTrigger, MenuValue } from '../../menus/src';
2
+ import { FloatingFocusManagerProps } from '@floating-ui/react';
3
+ import { default as React } from 'react';
4
+ interface VirtualSelectOption<T = unknown> {
5
+ value: string;
6
+ label: string;
7
+ disabled?: boolean;
8
+ data?: T;
9
+ }
10
+ interface VirtualSelectProps<T = unknown> {
11
+ className?: string;
12
+ closeOnEscape?: boolean;
13
+ disabled?: boolean;
14
+ emptyText?: string;
15
+ focusManagerProps?: Partial<FloatingFocusManagerProps>;
16
+ matchTriggerWidth?: boolean;
17
+ maxHeight?: number;
18
+ onChange?: (value: string) => void;
19
+ onOpenChange?: (open: boolean) => void;
20
+ open?: boolean;
21
+ options: VirtualSelectOption<T>[];
22
+ overscan?: number;
23
+ placeholder?: string;
24
+ placement?: "bottom-start" | "bottom-end";
25
+ portalId?: string;
26
+ readOnly?: boolean;
27
+ renderOption?: (option: VirtualSelectOption<T>, isSelected: boolean) => React.ReactNode;
28
+ renderValue?: (option: VirtualSelectOption<T> | null) => React.ReactNode;
29
+ root?: HTMLElement | null;
30
+ searchPlaceholder?: string;
31
+ size?: "default" | "large";
32
+ value?: string | null;
33
+ variant?: "default" | "light" | "reset";
34
+ }
35
+ interface VirtualSelectComponentType {
36
+ <T = unknown>(props: VirtualSelectProps<T>): React.ReactElement | null;
37
+ displayName?: string;
38
+ Content: typeof MenuContextContent;
39
+ Divider: typeof MenuDivider;
40
+ Empty: typeof MenuEmpty;
41
+ Item: typeof MenuContextItem;
42
+ Search: typeof MenuSearch;
43
+ Trigger: typeof MenuTrigger;
44
+ Value: typeof MenuValue;
45
+ }
46
+ declare const VirtualSelect: VirtualSelectComponentType;
47
+
48
+ export { VirtualSelect, type VirtualSelectOption, type VirtualSelectProps };
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@choice-ui/react",
3
- "version": "1.7.8",
3
+ "version": "1.8.0",
4
4
  "description": "A desktop-first React UI component library built for professional desktop applications with comprehensive documentation",
5
5
  "sideEffects": false,
6
6
  "type": "module",
7
7
  "source": "./app/index.ts",
8
8
  "main": "./dist/index.js",
9
- "types": "./app/index.ts",
9
+ "types": "./dist/index.d.ts",
10
10
  "files": [
11
11
  "dist",
12
12
  "README.md",
@@ -14,7 +14,7 @@
14
14
  ],
15
15
  "exports": {
16
16
  ".": {
17
- "types": "./app/index.ts",
17
+ "types": "./dist/index.d.ts",
18
18
  "import": "./dist/index.js"
19
19
  },
20
20
  "./styles/*": "./dist/styles/*",
@@ -22,36 +22,10 @@
22
22
  "./llms.txt": "./dist/llms.txt"
23
23
  },
24
24
  "publishConfig": {
25
- "access": "public",
26
- "types": "./dist/index.d.ts",
27
- "main": "./dist/index.js",
28
- "exports": {
29
- ".": {
30
- "types": "./dist/index.d.ts",
31
- "import": "./dist/index.js"
32
- },
33
- "./styles/*": "./dist/styles/*",
34
- "./tailwind.css": "./dist/tailwind.css",
35
- "./llms.txt": "./dist/llms.txt"
36
- }
37
- },
38
- "scripts": {
39
- "build": "pnpm run clean && pnpm --filter @choice-ui/shared build && vite build",
40
- "build:watch": "vite build --watch",
41
- "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,css,md}\"",
42
- "lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
43
- "lint:types": "tsc",
44
- "clean": "rimraf dist",
45
- "prepublishOnly": "pnpm run build",
46
- "plop": "pnpm dlx plop",
47
- "test": "jest",
48
- "test:watch": "jest --watch",
49
- "test:coverage": "jest --coverage",
50
- "madge": "npx madge --circular --extensions ts app/"
25
+ "access": "public"
51
26
  },
52
27
  "dependencies": {
53
28
  "@choiceform/icons-react": "^1.3.8",
54
- "@choice-ui/design-tokens": "workspace:*",
55
29
  "classnames": "^2.5.1",
56
30
  "tailwind-merge": "^3.3.1",
57
31
  "tailwind-variants": "^3.1.0",
@@ -85,7 +59,8 @@
85
59
  "remark-breaks": "^4.0.0",
86
60
  "remark-math": "^6.0.0",
87
61
  "harden-react-markdown": "^1.0.4",
88
- "shiki": "^3.9.2"
62
+ "shiki": "^3.9.2",
63
+ "@choice-ui/design-tokens": "0.2.13"
89
64
  },
90
65
  "devDependencies": {
91
66
  "@babel/core": "^7.27.1",
@@ -136,5 +111,18 @@
136
111
  "peerDependencies": {
137
112
  "react": ">=18.0.0",
138
113
  "react-dom": ">=18.0.0"
114
+ },
115
+ "scripts": {
116
+ "build": "pnpm run clean && pnpm --filter @choice-ui/shared build && vite build",
117
+ "build:watch": "vite build --watch",
118
+ "format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,css,md}\"",
119
+ "lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
120
+ "lint:types": "tsc",
121
+ "clean": "rimraf dist",
122
+ "plop": "pnpm dlx plop",
123
+ "test": "jest",
124
+ "test:watch": "jest --watch",
125
+ "test:coverage": "jest --coverage",
126
+ "madge": "npx madge --circular --extensions ts app/"
139
127
  }
140
- }
128
+ }