@mantine/emoji 0.0.0-stage → 9.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +20 -2
  3. package/cjs/adapters/emoji-mart-adapter.cjs +30 -0
  4. package/cjs/adapters/emoji-mart-adapter.cjs.map +1 -0
  5. package/cjs/adapters/emojibase-adapter.cjs +76 -0
  6. package/cjs/adapters/emojibase-adapter.cjs.map +1 -0
  7. package/cjs/categories.cjs +52 -0
  8. package/cjs/categories.cjs.map +1 -0
  9. package/cjs/components/EmojiPicker/EmojiButton.cjs +24 -0
  10. package/cjs/components/EmojiPicker/EmojiButton.cjs.map +1 -0
  11. package/cjs/components/EmojiPicker/EmojiPicker.cjs +289 -0
  12. package/cjs/components/EmojiPicker/EmojiPicker.cjs.map +1 -0
  13. package/cjs/components/EmojiPicker/EmojiPicker.context.cjs +7 -0
  14. package/cjs/components/EmojiPicker/EmojiPicker.context.cjs.map +1 -0
  15. package/cjs/components/EmojiPicker/EmojiPicker.module.cjs +22 -0
  16. package/cjs/components/EmojiPicker/EmojiPicker.module.cjs.map +1 -0
  17. package/cjs/components/EmojiPicker/EmojiPickerCategoryNav.cjs +38 -0
  18. package/cjs/components/EmojiPicker/EmojiPickerCategoryNav.cjs.map +1 -0
  19. package/cjs/components/EmojiPicker/EmojiPickerGrid.cjs +131 -0
  20. package/cjs/components/EmojiPicker/EmojiPickerGrid.cjs.map +1 -0
  21. package/cjs/components/EmojiPicker/EmojiPickerPreview.cjs +24 -0
  22. package/cjs/components/EmojiPicker/EmojiPickerPreview.cjs.map +1 -0
  23. package/cjs/components/EmojiPicker/EmojiPickerSearch.cjs +57 -0
  24. package/cjs/components/EmojiPicker/EmojiPickerSearch.cjs.map +1 -0
  25. package/cjs/components/EmojiPicker/EmojiPickerSkinTone.cjs +63 -0
  26. package/cjs/components/EmojiPicker/EmojiPickerSkinTone.cjs.map +1 -0
  27. package/cjs/components/EmojiPickerInput/EmojiPickerInput.cjs +190 -0
  28. package/cjs/components/EmojiPickerInput/EmojiPickerInput.cjs.map +1 -0
  29. package/cjs/components/EmojiPickerInput/EmojiPickerInput.module.cjs +11 -0
  30. package/cjs/components/EmojiPickerInput/EmojiPickerInput.module.cjs.map +1 -0
  31. package/cjs/components/EmojiPickerPopover/EmojiPickerPopover.cjs +59 -0
  32. package/cjs/components/EmojiPickerPopover/EmojiPickerPopover.cjs.map +1 -0
  33. package/cjs/components/EmojiPickerPopover/EmojiPickerPopover.module.cjs +6 -0
  34. package/cjs/components/EmojiPickerPopover/EmojiPickerPopover.module.cjs.map +1 -0
  35. package/cjs/hooks/use-emoji-grid-navigation.cjs +70 -0
  36. package/cjs/hooks/use-emoji-grid-navigation.cjs.map +1 -0
  37. package/cjs/hooks/use-emoji-search.cjs +21 -0
  38. package/cjs/hooks/use-emoji-search.cjs.map +1 -0
  39. package/cjs/index.cjs +22 -0
  40. package/cjs/types.cjs +20 -0
  41. package/cjs/types.cjs.map +1 -0
  42. package/cjs/utils/emoji-grid-utils.cjs +57 -0
  43. package/cjs/utils/emoji-grid-utils.cjs.map +1 -0
  44. package/cjs/utils/flatten-emoji-data.cjs +33 -0
  45. package/cjs/utils/flatten-emoji-data.cjs.map +1 -0
  46. package/cjs/utils/is-emoji-supported.cjs +64 -0
  47. package/cjs/utils/is-emoji-supported.cjs.map +1 -0
  48. package/cjs/utils/search-emojis.cjs +21 -0
  49. package/cjs/utils/search-emojis.cjs.map +1 -0
  50. package/esm/adapters/emoji-mart-adapter.mjs +30 -0
  51. package/esm/adapters/emoji-mart-adapter.mjs.map +1 -0
  52. package/esm/adapters/emojibase-adapter.mjs +76 -0
  53. package/esm/adapters/emojibase-adapter.mjs.map +1 -0
  54. package/esm/categories.mjs +47 -0
  55. package/esm/categories.mjs.map +1 -0
  56. package/esm/components/EmojiPicker/EmojiButton.mjs +24 -0
  57. package/esm/components/EmojiPicker/EmojiButton.mjs.map +1 -0
  58. package/esm/components/EmojiPicker/EmojiPicker.context.mjs +7 -0
  59. package/esm/components/EmojiPicker/EmojiPicker.context.mjs.map +1 -0
  60. package/esm/components/EmojiPicker/EmojiPicker.mjs +289 -0
  61. package/esm/components/EmojiPicker/EmojiPicker.mjs.map +1 -0
  62. package/esm/components/EmojiPicker/EmojiPicker.module.mjs +22 -0
  63. package/esm/components/EmojiPicker/EmojiPicker.module.mjs.map +1 -0
  64. package/esm/components/EmojiPicker/EmojiPickerCategoryNav.mjs +38 -0
  65. package/esm/components/EmojiPicker/EmojiPickerCategoryNav.mjs.map +1 -0
  66. package/esm/components/EmojiPicker/EmojiPickerGrid.mjs +131 -0
  67. package/esm/components/EmojiPicker/EmojiPickerGrid.mjs.map +1 -0
  68. package/esm/components/EmojiPicker/EmojiPickerPreview.mjs +24 -0
  69. package/esm/components/EmojiPicker/EmojiPickerPreview.mjs.map +1 -0
  70. package/esm/components/EmojiPicker/EmojiPickerSearch.mjs +57 -0
  71. package/esm/components/EmojiPicker/EmojiPickerSearch.mjs.map +1 -0
  72. package/esm/components/EmojiPicker/EmojiPickerSkinTone.mjs +63 -0
  73. package/esm/components/EmojiPicker/EmojiPickerSkinTone.mjs.map +1 -0
  74. package/esm/components/EmojiPickerInput/EmojiPickerInput.mjs +190 -0
  75. package/esm/components/EmojiPickerInput/EmojiPickerInput.mjs.map +1 -0
  76. package/esm/components/EmojiPickerInput/EmojiPickerInput.module.mjs +11 -0
  77. package/esm/components/EmojiPickerInput/EmojiPickerInput.module.mjs.map +1 -0
  78. package/esm/components/EmojiPickerPopover/EmojiPickerPopover.mjs +59 -0
  79. package/esm/components/EmojiPickerPopover/EmojiPickerPopover.mjs.map +1 -0
  80. package/esm/components/EmojiPickerPopover/EmojiPickerPopover.module.mjs +6 -0
  81. package/esm/components/EmojiPickerPopover/EmojiPickerPopover.module.mjs.map +1 -0
  82. package/esm/hooks/use-emoji-grid-navigation.mjs +70 -0
  83. package/esm/hooks/use-emoji-grid-navigation.mjs.map +1 -0
  84. package/esm/hooks/use-emoji-search.mjs +21 -0
  85. package/esm/hooks/use-emoji-search.mjs.map +1 -0
  86. package/esm/index.mjs +11 -0
  87. package/esm/types.mjs +20 -0
  88. package/esm/types.mjs.map +1 -0
  89. package/esm/utils/emoji-grid-utils.mjs +54 -0
  90. package/esm/utils/emoji-grid-utils.mjs.map +1 -0
  91. package/esm/utils/flatten-emoji-data.mjs +33 -0
  92. package/esm/utils/flatten-emoji-data.mjs.map +1 -0
  93. package/esm/utils/is-emoji-supported.mjs +63 -0
  94. package/esm/utils/is-emoji-supported.mjs.map +1 -0
  95. package/esm/utils/search-emojis.mjs +21 -0
  96. package/esm/utils/search-emojis.mjs.map +1 -0
  97. package/lib/adapters/emoji-mart-adapter.d.ts +23 -0
  98. package/lib/adapters/emojibase-adapter.d.ts +25 -0
  99. package/lib/adapters/index.d.ts +2 -0
  100. package/lib/categories.d.ts +7 -0
  101. package/lib/components/EmojiPicker/EmojiButton.d.ts +10 -0
  102. package/lib/components/EmojiPicker/EmojiPicker.context.d.ts +9 -0
  103. package/lib/components/EmojiPicker/EmojiPicker.d.ts +64 -0
  104. package/lib/components/EmojiPicker/EmojiPickerCategoryNav.d.ts +10 -0
  105. package/lib/components/EmojiPicker/EmojiPickerGrid.d.ts +23 -0
  106. package/lib/components/EmojiPicker/EmojiPickerPreview.d.ts +9 -0
  107. package/lib/components/EmojiPicker/EmojiPickerSearch.d.ts +14 -0
  108. package/lib/components/EmojiPicker/EmojiPickerSkinTone.d.ts +6 -0
  109. package/lib/components/EmojiPicker/index.d.ts +2 -0
  110. package/lib/components/EmojiPickerInput/EmojiPickerInput.d.ts +54 -0
  111. package/lib/components/EmojiPickerInput/index.d.ts +2 -0
  112. package/lib/components/EmojiPickerPopover/EmojiPickerPopover.d.ts +24 -0
  113. package/lib/components/EmojiPickerPopover/index.d.ts +2 -0
  114. package/lib/hooks/index.d.ts +2 -0
  115. package/lib/hooks/use-emoji-grid-navigation.d.ts +18 -0
  116. package/lib/hooks/use-emoji-search.d.ts +14 -0
  117. package/lib/index.d.mts +10 -0
  118. package/lib/index.d.ts +10 -0
  119. package/lib/types.d.ts +41 -0
  120. package/lib/utils/emoji-grid-utils.d.ts +17 -0
  121. package/lib/utils/flatten-emoji-data.d.ts +2 -0
  122. package/lib/utils/index.d.ts +5 -0
  123. package/lib/utils/is-emoji-supported.d.ts +4 -0
  124. package/lib/utils/search-emojis.d.ts +2 -0
  125. package/package.json +54 -4
  126. package/styles.css +228 -0
  127. package/styles.layer.css +229 -0
@@ -0,0 +1,24 @@
1
+ "use client";
2
+ import { applySkinTone } from "../../utils/emoji-grid-utils.mjs";
3
+ import { useEmojiPickerContext } from "./EmojiPicker.context.mjs";
4
+ import { memo } from "react";
5
+ import { UnstyledButton } from "@mantine/core";
6
+ import { jsx } from "react/jsx-runtime";
7
+ // packages/@mantine/emoji/src/components/EmojiPicker/EmojiButton.tsx
8
+ const EmojiButton = memo(function EmojiButton({ emoji, skinTone, emojiIndex, focused, id }) {
9
+ const ctx = useEmojiPickerContext();
10
+ return /* @__PURE__ */ jsx(UnstyledButton, {
11
+ ...ctx.getStyles("emoji"),
12
+ role: "gridcell",
13
+ "aria-label": emoji.name,
14
+ title: emoji.name,
15
+ id,
16
+ tabIndex: -1,
17
+ "data-focused": focused || void 0,
18
+ "data-emoji-index": emojiIndex,
19
+ children: applySkinTone(emoji, skinTone).emoji
20
+ });
21
+ });
22
+ export { EmojiButton };
23
+
24
+ //# sourceMappingURL=EmojiButton.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmojiButton.mjs","names":[],"sources":["../../../src/components/EmojiPicker/EmojiButton.tsx"],"sourcesContent":["import { memo } from 'react';\nimport { UnstyledButton } from '@mantine/core';\nimport type { EmojiPickerEmoji } from '../../types';\nimport { applySkinTone } from '../../utils';\nimport { useEmojiPickerContext } from './EmojiPicker.context';\n\ninterface EmojiButtonProps {\n emoji: EmojiPickerEmoji;\n skinTone: number;\n emojiIndex: number;\n focused: boolean;\n id: string;\n}\n\nexport const EmojiButton = memo(function EmojiButton({\n emoji,\n skinTone,\n emojiIndex,\n focused,\n id,\n}: EmojiButtonProps) {\n const ctx = useEmojiPickerContext();\n\n return (\n <UnstyledButton\n {...ctx.getStyles('emoji')}\n role=\"gridcell\"\n aria-label={emoji.name}\n title={emoji.name}\n id={id}\n tabIndex={-1}\n data-focused={focused || undefined}\n data-emoji-index={emojiIndex}\n >\n {applySkinTone(emoji, skinTone).emoji}\n </UnstyledButton>\n );\n});\n"],"mappings":";;;;;;;AAcA,MAAa,cAAc,KAAK,SAAS,YAAY,EACnD,OACA,UACA,YACA,SACA,MACmB;CACnB,MAAM,MAAM,sBAAsB;CAElC,OACE,oBAAC,gBAAD;EACE,GAAI,IAAI,UAAU,OAAO;EACzB,MAAK;EACL,cAAY,MAAM;EAClB,OAAO,MAAM;EACT;EACJ,UAAU;EACV,gBAAc,WAAW,KAAA;EACzB,oBAAkB;EAEjB,UAAA,cAAc,OAAO,QAAQ,CAAC,CAAC;CAClB,CAAA;AAEpB,CAAC"}
@@ -0,0 +1,7 @@
1
+ "use client";
2
+ import { createSafeContext } from "@mantine/core";
3
+ // packages/@mantine/emoji/src/components/EmojiPicker/EmojiPicker.context.ts
4
+ const [EmojiPickerProvider, useEmojiPickerContext] = createSafeContext("EmojiPicker components must be used within EmojiPicker");
5
+ export { EmojiPickerProvider, useEmojiPickerContext };
6
+
7
+ //# sourceMappingURL=EmojiPicker.context.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmojiPicker.context.mjs","names":[],"sources":["../../../src/components/EmojiPicker/EmojiPicker.context.ts"],"sourcesContent":["import { createSafeContext, type GetStylesApi, type MantineSize } from '@mantine/core';\nimport type { EmojiPickerLabels } from '../../types';\nimport type { EmojiPickerFactory } from './EmojiPicker';\n\nexport interface EmojiPickerContextValue {\n getStyles: GetStylesApi<EmojiPickerFactory>;\n labels: Required<EmojiPickerLabels>;\n size: MantineSize | undefined;\n}\n\nexport const [EmojiPickerProvider, useEmojiPickerContext] =\n createSafeContext<EmojiPickerContextValue>(\n 'EmojiPicker components must be used within EmojiPicker'\n );\n"],"mappings":";;;AAUA,MAAa,CAAC,qBAAqB,yBACjC,kBACE,wDACF"}
@@ -0,0 +1,289 @@
1
+ "use client";
2
+ import { DEFAULT_LABELS } from "../../types.mjs";
3
+ import { flattenEmojiData } from "../../utils/flatten-emoji-data.mjs";
4
+ import { filterSupportedEmojis } from "../../utils/is-emoji-supported.mjs";
5
+ import { applySkinTone, getEmojiItemIndex } from "../../utils/emoji-grid-utils.mjs";
6
+ import { useEmojiGridNavigation } from "../../hooks/use-emoji-grid-navigation.mjs";
7
+ import { useEmojiSearch } from "../../hooks/use-emoji-search.mjs";
8
+ import { EmojiPickerProvider } from "./EmojiPicker.context.mjs";
9
+ import { EmojiPickerCategoryNav } from "./EmojiPickerCategoryNav.mjs";
10
+ import { EmojiPickerGrid } from "./EmojiPickerGrid.mjs";
11
+ import { EmojiPickerPreview } from "./EmojiPickerPreview.mjs";
12
+ import { EmojiPickerSearch } from "./EmojiPickerSearch.mjs";
13
+ import { EmojiPickerSkinTone } from "./EmojiPickerSkinTone.mjs";
14
+ import EmojiPicker_module_default from "./EmojiPicker.module.mjs";
15
+ import { useCallback, useEffect, useId, useMemo, useRef, useState } from "react";
16
+ import { Box, createVarsResolver, factory, rem, useProps, useStyles } from "@mantine/core";
17
+ import { useUncontrolled } from "@mantine/hooks";
18
+ import { jsx, jsxs } from "react/jsx-runtime";
19
+ // packages/@mantine/emoji/src/components/EmojiPicker/EmojiPicker.tsx
20
+ const defaultProps = {
21
+ columns: 9,
22
+ withSearch: true,
23
+ withCategoryNav: true,
24
+ withPreview: true,
25
+ withSkinToneSelector: true,
26
+ withUnsupportedDetection: false,
27
+ autoFocus: false,
28
+ size: "sm"
29
+ };
30
+ const EMOJI_SIZES = {
31
+ xs: {
32
+ emojiSize: 28,
33
+ emojiFz: rem(16),
34
+ previewPy: rem(4),
35
+ skinToneSize: rem(20),
36
+ skinToneFz: rem(13)
37
+ },
38
+ sm: {
39
+ emojiSize: 32,
40
+ emojiFz: rem(18),
41
+ previewPy: rem(5),
42
+ skinToneSize: rem(22),
43
+ skinToneFz: rem(14)
44
+ },
45
+ md: {
46
+ emojiSize: 36,
47
+ emojiFz: rem(22),
48
+ previewPy: rem(7),
49
+ skinToneSize: rem(24),
50
+ skinToneFz: rem(16)
51
+ },
52
+ lg: {
53
+ emojiSize: 42,
54
+ emojiFz: rem(26),
55
+ previewPy: rem(8),
56
+ skinToneSize: rem(28),
57
+ skinToneFz: rem(18)
58
+ },
59
+ xl: {
60
+ emojiSize: 48,
61
+ emojiFz: rem(30),
62
+ previewPy: rem(10),
63
+ skinToneSize: rem(32),
64
+ skinToneFz: rem(20)
65
+ }
66
+ };
67
+ const EMOJI_FONT_SIZE_RATIO = .6;
68
+ function getSizeValues(size) {
69
+ return EMOJI_SIZES[size || "sm"] || EMOJI_SIZES.sm;
70
+ }
71
+ const varsResolver = createVarsResolver((_, { columns, emojiSize, size }) => {
72
+ const sizeValues = getSizeValues(size);
73
+ return { root: {
74
+ "--ep-columns": columns?.toString() ?? "9",
75
+ "--ep-emoji-size": emojiSize ? rem(emojiSize) : rem(sizeValues.emojiSize),
76
+ "--ep-emoji-fz": emojiSize ? rem(Math.round(emojiSize * EMOJI_FONT_SIZE_RATIO)) : sizeValues.emojiFz,
77
+ "--ep-preview-py": sizeValues.previewPy,
78
+ "--ep-skin-tone-size": sizeValues.skinToneSize,
79
+ "--ep-skin-tone-fz": sizeValues.skinToneFz
80
+ } };
81
+ });
82
+ const EmojiPicker = factory((_props) => {
83
+ const props = useProps("EmojiPicker", defaultProps, _props);
84
+ const { classNames, className, style, styles, unstyled, vars, data, onEmojiSelect, columns, emojiSize, withSearch, withCategoryNav, withPreview, withSkinToneSelector, skinTone: skinToneProp, defaultSkinTone, onSkinToneChange, searchValue, onSearchChange, searchPlaceholder, emptySearchContent, withUnsupportedDetection, autoFocus, scrollAreaProps, categoryLabels, labels: labelsProp, size, searchInputProps, ...others } = props;
85
+ const getStyles = useStyles({
86
+ name: "EmojiPicker",
87
+ classes: EmojiPicker_module_default,
88
+ props,
89
+ classNames,
90
+ styles,
91
+ unstyled,
92
+ vars,
93
+ varsResolver,
94
+ stable: true
95
+ });
96
+ const [skinTone, setSkinTone] = useUncontrolled({
97
+ value: skinToneProp,
98
+ defaultValue: defaultSkinTone ?? 0,
99
+ onChange: onSkinToneChange
100
+ });
101
+ const cols = Math.max(columns ?? 9, 1);
102
+ const baseId = useId();
103
+ const idPrefix = `${baseId}-emoji`;
104
+ const gridId = `${baseId}-grid`;
105
+ const [activeDescendant, setActiveDescendant] = useState(void 0);
106
+ const filteredData = useMemo(() => {
107
+ if (!withUnsupportedDetection) return data;
108
+ return {
109
+ emojis: data.emojis,
110
+ categories: data.categories.map((cat) => ({
111
+ ...cat,
112
+ emojis: filterSupportedEmojis(data.emojis, cat.emojis)
113
+ }))
114
+ };
115
+ }, [data, withUnsupportedDetection]);
116
+ const { query, setQuery, results, isSearching } = useEmojiSearch({
117
+ data: filteredData,
118
+ searchValue,
119
+ onSearchChange
120
+ });
121
+ const searchInputRef = useRef(null);
122
+ const scrollToItemRef = useRef(null);
123
+ const mergedLabels = useMemo(() => ({
124
+ ...DEFAULT_LABELS,
125
+ ...labelsProp
126
+ }), [labelsProp]);
127
+ const ctxValue = useMemo(() => ({
128
+ getStyles,
129
+ labels: mergedLabels,
130
+ size
131
+ }), [
132
+ getStyles,
133
+ mergedLabels,
134
+ size
135
+ ]);
136
+ const categoryItems = useMemo(() => flattenEmojiData(filteredData, cols, categoryLabels), [
137
+ filteredData,
138
+ cols,
139
+ categoryLabels
140
+ ]);
141
+ const searchItems = useMemo(() => flattenEmojiData({
142
+ emojis: filteredData.emojis,
143
+ categories: [{
144
+ id: "search",
145
+ label: mergedLabels.searchResults,
146
+ emojis: results.map((emoji) => emoji.id)
147
+ }]
148
+ }, cols), [
149
+ filteredData.emojis,
150
+ cols,
151
+ results,
152
+ mergedLabels.searchResults
153
+ ]);
154
+ const flattenedItems = isSearching ? searchItems : categoryItems;
155
+ const navCategories = useMemo(() => categoryItems.reduce((acc, item) => {
156
+ if (item.type === "header") acc.push({
157
+ id: item.categoryId,
158
+ label: item.label
159
+ });
160
+ return acc;
161
+ }, []), [categoryItems]);
162
+ const scrollToItem = useCallback((itemIndex) => {
163
+ scrollToItemRef.current?.(itemIndex, "auto");
164
+ }, []);
165
+ const handleEmojiSelect = useCallback((emoji) => {
166
+ onEmojiSelect?.(emoji);
167
+ }, [onEmojiSelect]);
168
+ const { model, activeIndex, activeEmoji, keyboardActive, handleKeyDown, setPointerIndex, setAnchorIndex } = useEmojiGridNavigation({
169
+ items: flattenedItems,
170
+ resetKey: query,
171
+ skinTone,
172
+ onEmojiSelect: handleEmojiSelect,
173
+ scrollToItem
174
+ });
175
+ const handleGridKeyDown = useCallback((event) => {
176
+ if (event.key === "Escape") {
177
+ const focusStayedInGrid = event.currentTarget.contains(document.activeElement);
178
+ if (withSearch && focusStayedInGrid && searchInputRef.current) {
179
+ event.preventDefault();
180
+ searchInputRef.current.focus();
181
+ }
182
+ return;
183
+ }
184
+ handleKeyDown(event);
185
+ }, [handleKeyDown, withSearch]);
186
+ const handleSearchKeyDown = useCallback((event) => {
187
+ if (event.nativeEvent.isComposing) return;
188
+ if (event.key === "ArrowDown" || event.key === "ArrowUp" || event.key === "Enter") handleKeyDown(event);
189
+ }, [handleKeyDown]);
190
+ const handleEmojiClick = useCallback((index) => {
191
+ const emoji = model.emojis[index];
192
+ if (emoji) {
193
+ setPointerIndex(index);
194
+ handleEmojiSelect(applySkinTone(emoji, skinTone));
195
+ }
196
+ }, [
197
+ model,
198
+ setPointerIndex,
199
+ handleEmojiSelect,
200
+ skinTone
201
+ ]);
202
+ const scrollToCategory = useCallback((categoryId) => {
203
+ const itemIndex = flattenedItems.findIndex((item) => item.type === "header" && item.categoryId === categoryId);
204
+ if (itemIndex !== -1) {
205
+ setAnchorIndex(model.itemOffsets[itemIndex]);
206
+ scrollToItemRef.current?.(itemIndex, "start");
207
+ }
208
+ }, [
209
+ flattenedItems,
210
+ model,
211
+ setAnchorIndex
212
+ ]);
213
+ const pendingCategoryRef = useRef(null);
214
+ const handleCategoryClick = useCallback((categoryId) => {
215
+ if (isSearching) {
216
+ pendingCategoryRef.current = categoryId;
217
+ setQuery("");
218
+ return;
219
+ }
220
+ scrollToCategory(categoryId);
221
+ }, [
222
+ isSearching,
223
+ setQuery,
224
+ scrollToCategory
225
+ ]);
226
+ useEffect(() => {
227
+ if (!isSearching && pendingCategoryRef.current !== null) {
228
+ const categoryId = pendingCategoryRef.current;
229
+ pendingCategoryRef.current = null;
230
+ scrollToCategory(categoryId);
231
+ }
232
+ }, [isSearching, scrollToCategory]);
233
+ return /* @__PURE__ */ jsx(EmojiPickerProvider, {
234
+ value: ctxValue,
235
+ children: /* @__PURE__ */ jsxs(Box, {
236
+ ...getStyles("root"),
237
+ ...others,
238
+ children: [
239
+ withSearch && /* @__PURE__ */ jsx(EmojiPickerSearch, {
240
+ query,
241
+ onQueryChange: setQuery,
242
+ placeholder: searchPlaceholder,
243
+ autoFocus,
244
+ inputRef: searchInputRef,
245
+ onKeyDown: handleSearchKeyDown,
246
+ controls: flattenedItems.length > 0 ? gridId : void 0,
247
+ activeDescendant,
248
+ searchInputProps
249
+ }),
250
+ withCategoryNav && /* @__PURE__ */ jsx(EmojiPickerCategoryNav, {
251
+ categories: navCategories,
252
+ onCategoryClick: handleCategoryClick
253
+ }),
254
+ /* @__PURE__ */ jsx(EmojiPickerGrid, {
255
+ items: flattenedItems,
256
+ itemOffsets: model.itemOffsets,
257
+ columns: cols,
258
+ skinTone,
259
+ focusedIndex: keyboardActive ? activeIndex : -1,
260
+ focusedItemIndex: keyboardActive ? getEmojiItemIndex(model, activeIndex) : -1,
261
+ idPrefix,
262
+ gridId,
263
+ onActiveDescendantChange: setActiveDescendant,
264
+ emojiSize: emojiSize ?? getSizeValues(size).emojiSize,
265
+ emptyContent: emptySearchContent || mergedLabels.noResults,
266
+ onKeyDown: handleGridKeyDown,
267
+ onPointerIndex: setPointerIndex,
268
+ onEmojiClick: handleEmojiClick,
269
+ scrollToItemRef,
270
+ scrollAreaProps
271
+ }),
272
+ (withPreview || withSkinToneSelector) && /* @__PURE__ */ jsx(EmojiPickerPreview, {
273
+ emoji: activeEmoji,
274
+ skinTone,
275
+ withPreview: !!withPreview,
276
+ children: withSkinToneSelector && /* @__PURE__ */ jsx(EmojiPickerSkinTone, {
277
+ skinTone,
278
+ onSkinToneChange: setSkinTone
279
+ })
280
+ })
281
+ ]
282
+ })
283
+ });
284
+ });
285
+ EmojiPicker.classes = EmojiPicker_module_default;
286
+ EmojiPicker.displayName = "@mantine/emoji/EmojiPicker";
287
+ export { EmojiPicker };
288
+
289
+ //# sourceMappingURL=EmojiPicker.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmojiPicker.mjs","names":["classes"],"sources":["../../../src/components/EmojiPicker/EmojiPicker.tsx"],"sourcesContent":["import { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';\nimport {\n Box,\n BoxProps,\n createVarsResolver,\n ElementProps,\n factory,\n Factory,\n InputProps,\n MantineSize,\n rem,\n ScrollAreaProps,\n StylesApiProps,\n useProps,\n useStyles,\n} from '@mantine/core';\nimport { useUncontrolled } from '@mantine/hooks';\nimport { useEmojiGridNavigation } from '../../hooks/use-emoji-grid-navigation';\nimport { useEmojiSearch } from '../../hooks/use-emoji-search';\nimport {\n DEFAULT_LABELS,\n EmojiPickerData,\n EmojiPickerEmoji,\n EmojiPickerLabels,\n FlattenedRowItem,\n} from '../../types';\nimport {\n applySkinTone,\n filterSupportedEmojis,\n flattenEmojiData,\n getEmojiItemIndex,\n} from '../../utils';\nimport { EmojiPickerProvider } from './EmojiPicker.context';\nimport { EmojiPickerCategoryNav, EmojiPickerNavCategory } from './EmojiPickerCategoryNav';\nimport { EmojiPickerGrid, EmojiPickerScrollToItem } from './EmojiPickerGrid';\nimport { EmojiPickerPreview } from './EmojiPickerPreview';\nimport { EmojiPickerSearch } from './EmojiPickerSearch';\nimport { EmojiPickerSkinTone } from './EmojiPickerSkinTone';\nimport classes from './EmojiPicker.module.css';\n\nexport type EmojiPickerStylesNames =\n | 'root'\n | 'search'\n | 'searchInput'\n | 'categoryNav'\n | 'categoryNavButton'\n | 'content'\n | 'empty'\n | 'categoryLabel'\n | 'grid'\n | 'emoji'\n | 'preview'\n | 'previewEmoji'\n | 'previewName'\n | 'skinToneSelector'\n | 'skinToneDropdown'\n | 'skinToneButton';\n\nexport type EmojiPickerCssVariables = {\n root:\n | '--ep-columns'\n | '--ep-emoji-size'\n | '--ep-emoji-fz'\n | '--ep-preview-py'\n | '--ep-skin-tone-size'\n | '--ep-skin-tone-fz';\n};\n\nexport interface EmojiPickerProps\n extends BoxProps, StylesApiProps<EmojiPickerFactory>, ElementProps<'div'> {\n /** Emoji data object, use `emojiMartAdapter` or `emojibaseAdapter` to convert data */\n data: EmojiPickerData;\n\n /** Called when an emoji is selected */\n onEmojiSelect?: (emoji: EmojiPickerEmoji) => void;\n\n /** Number of emoji columns @default 9 */\n columns?: number;\n\n /** Custom emoji button size in px, overrides size-based value */\n emojiSize?: number;\n\n /** Determines whether the search input is displayed @default true */\n withSearch?: boolean;\n\n /** Determines whether the category navigation bar is displayed @default true */\n withCategoryNav?: boolean;\n\n /** Determines whether the emoji preview area is displayed @default true */\n withPreview?: boolean;\n\n /** Determines whether the skin tone selector is displayed @default true */\n withSkinToneSelector?: boolean;\n\n /** Controlled skin tone value (0–5) */\n skinTone?: number;\n\n /** Default skin tone value for uncontrolled state @default 0 */\n defaultSkinTone?: number;\n\n /** Called when skin tone changes */\n onSkinToneChange?: (skinTone: number) => void;\n\n /** Controlled search input value */\n searchValue?: string;\n\n /** Called when search value changes */\n onSearchChange?: (value: string) => void;\n\n /** Search input placeholder */\n searchPlaceholder?: string;\n\n /** Content displayed when search yields no results */\n emptySearchContent?: React.ReactNode;\n\n /** Determines whether unsupported emojis are filtered out @default false */\n withUnsupportedDetection?: boolean;\n\n /** Determines whether the search input is focused on mount @default false */\n autoFocus?: boolean;\n\n /** Props passed to the internal `ScrollArea` component */\n scrollAreaProps?: ScrollAreaProps;\n\n /** Override category labels by category id */\n categoryLabels?: Record<string, string>;\n\n /** Override UI labels (search placeholder, no results text, etc.) */\n labels?: EmojiPickerLabels;\n\n /** Controls size of all inner elements @default 'sm' */\n size?: MantineSize;\n\n /** Props passed to the search `Input` component */\n searchInputProps?: InputProps;\n}\n\nexport type EmojiPickerFactory = Factory<{\n props: EmojiPickerProps;\n ref: HTMLDivElement;\n stylesNames: EmojiPickerStylesNames;\n vars: EmojiPickerCssVariables;\n}>;\n\nconst defaultProps = {\n columns: 9,\n withSearch: true,\n withCategoryNav: true,\n withPreview: true,\n withSkinToneSelector: true,\n withUnsupportedDetection: false,\n autoFocus: false,\n size: 'sm',\n} satisfies Partial<EmojiPickerProps>;\n\nconst EMOJI_SIZES: Record<\n string,\n {\n emojiSize: number;\n emojiFz: string;\n previewPy: string;\n skinToneSize: string;\n skinToneFz: string;\n }\n> = {\n xs: {\n emojiSize: 28,\n emojiFz: rem(16),\n previewPy: rem(4),\n skinToneSize: rem(20),\n skinToneFz: rem(13),\n },\n sm: {\n emojiSize: 32,\n emojiFz: rem(18),\n previewPy: rem(5),\n skinToneSize: rem(22),\n skinToneFz: rem(14),\n },\n md: {\n emojiSize: 36,\n emojiFz: rem(22),\n previewPy: rem(7),\n skinToneSize: rem(24),\n skinToneFz: rem(16),\n },\n lg: {\n emojiSize: 42,\n emojiFz: rem(26),\n previewPy: rem(8),\n skinToneSize: rem(28),\n skinToneFz: rem(18),\n },\n xl: {\n emojiSize: 48,\n emojiFz: rem(30),\n previewPy: rem(10),\n skinToneSize: rem(32),\n skinToneFz: rem(20),\n },\n};\n\nconst EMOJI_FONT_SIZE_RATIO = 0.6;\n\nfunction getSizeValues(size: MantineSize | undefined) {\n return EMOJI_SIZES[size || 'sm'] || EMOJI_SIZES.sm;\n}\n\nconst varsResolver = createVarsResolver<EmojiPickerFactory>((_, { columns, emojiSize, size }) => {\n const sizeValues = getSizeValues(size);\n return {\n root: {\n '--ep-columns': columns?.toString() ?? '9',\n '--ep-emoji-size': emojiSize ? rem(emojiSize) : rem(sizeValues.emojiSize),\n '--ep-emoji-fz': emojiSize\n ? rem(Math.round(emojiSize * EMOJI_FONT_SIZE_RATIO))\n : sizeValues.emojiFz,\n '--ep-preview-py': sizeValues.previewPy,\n '--ep-skin-tone-size': sizeValues.skinToneSize,\n '--ep-skin-tone-fz': sizeValues.skinToneFz,\n },\n };\n});\n\nexport const EmojiPicker = factory<EmojiPickerFactory>((_props) => {\n const props = useProps('EmojiPicker', defaultProps, _props);\n const {\n classNames,\n className,\n style,\n styles,\n unstyled,\n vars,\n data,\n onEmojiSelect,\n columns,\n emojiSize,\n withSearch,\n withCategoryNav,\n withPreview,\n withSkinToneSelector,\n skinTone: skinToneProp,\n defaultSkinTone,\n onSkinToneChange,\n searchValue,\n onSearchChange,\n searchPlaceholder,\n emptySearchContent,\n withUnsupportedDetection,\n autoFocus,\n scrollAreaProps,\n categoryLabels,\n labels: labelsProp,\n size,\n searchInputProps,\n ...others\n } = props;\n\n const getStyles = useStyles<EmojiPickerFactory>({\n name: 'EmojiPicker',\n classes,\n props,\n classNames,\n styles,\n unstyled,\n vars,\n varsResolver,\n stable: true,\n });\n\n const [skinTone, setSkinTone] = useUncontrolled({\n value: skinToneProp,\n defaultValue: defaultSkinTone ?? 0,\n onChange: onSkinToneChange,\n });\n\n const cols = Math.max(columns ?? 9, 1);\n const baseId = useId();\n const idPrefix = `${baseId}-emoji`;\n const gridId = `${baseId}-grid`;\n const [activeDescendant, setActiveDescendant] = useState<string | undefined>(undefined);\n\n const filteredData = useMemo(() => {\n if (!withUnsupportedDetection) {\n return data;\n }\n return {\n emojis: data.emojis,\n categories: data.categories.map((cat) => ({\n ...cat,\n emojis: filterSupportedEmojis(data.emojis, cat.emojis),\n })),\n };\n }, [data, withUnsupportedDetection]);\n\n const { query, setQuery, results, isSearching } = useEmojiSearch({\n data: filteredData,\n searchValue,\n onSearchChange,\n });\n\n const searchInputRef = useRef<HTMLInputElement>(null);\n const scrollToItemRef = useRef<EmojiPickerScrollToItem | null>(null);\n\n const mergedLabels: Required<EmojiPickerLabels> = useMemo(\n () => ({ ...DEFAULT_LABELS, ...labelsProp }),\n [labelsProp]\n );\n\n const ctxValue = useMemo(\n () => ({ getStyles, labels: mergedLabels, size }),\n [getStyles, mergedLabels, size]\n );\n\n const categoryItems = useMemo<FlattenedRowItem[]>(\n () => flattenEmojiData(filteredData, cols, categoryLabels),\n [filteredData, cols, categoryLabels]\n );\n\n const searchItems = useMemo<FlattenedRowItem[]>(\n () =>\n flattenEmojiData(\n {\n emojis: filteredData.emojis,\n categories: [\n {\n id: 'search',\n label: mergedLabels.searchResults,\n emojis: results.map((emoji) => emoji.id),\n },\n ],\n },\n cols\n ),\n [filteredData.emojis, cols, results, mergedLabels.searchResults]\n );\n\n const flattenedItems = isSearching ? searchItems : categoryItems;\n\n const navCategories = useMemo(\n () =>\n categoryItems.reduce<EmojiPickerNavCategory[]>((acc, item) => {\n if (item.type === 'header') {\n acc.push({ id: item.categoryId, label: item.label });\n }\n return acc;\n }, []),\n [categoryItems]\n );\n\n const scrollToItem = useCallback((itemIndex: number) => {\n scrollToItemRef.current?.(itemIndex, 'auto');\n }, []);\n\n const handleEmojiSelect = useCallback(\n (emoji: EmojiPickerEmoji) => {\n onEmojiSelect?.(emoji);\n },\n [onEmojiSelect]\n );\n\n const {\n model,\n activeIndex,\n activeEmoji,\n keyboardActive,\n handleKeyDown,\n setPointerIndex,\n setAnchorIndex,\n } = useEmojiGridNavigation({\n items: flattenedItems,\n resetKey: query,\n skinTone,\n onEmojiSelect: handleEmojiSelect,\n scrollToItem,\n });\n\n const handleGridKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLDivElement>) => {\n if (event.key === 'Escape') {\n const focusStayedInGrid = event.currentTarget.contains(document.activeElement);\n if (withSearch && focusStayedInGrid && searchInputRef.current) {\n event.preventDefault();\n searchInputRef.current.focus();\n }\n return;\n }\n handleKeyDown(event);\n },\n [handleKeyDown, withSearch]\n );\n\n const handleSearchKeyDown = useCallback(\n (event: React.KeyboardEvent<HTMLInputElement>) => {\n if (event.nativeEvent.isComposing) {\n return;\n }\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp' || event.key === 'Enter') {\n handleKeyDown(event);\n }\n },\n [handleKeyDown]\n );\n\n const handleEmojiClick = useCallback(\n (index: number) => {\n const emoji = model.emojis[index];\n if (emoji) {\n setPointerIndex(index);\n handleEmojiSelect(applySkinTone(emoji, skinTone));\n }\n },\n [model, setPointerIndex, handleEmojiSelect, skinTone]\n );\n\n const scrollToCategory = useCallback(\n (categoryId: string) => {\n const itemIndex = flattenedItems.findIndex(\n (item) => item.type === 'header' && item.categoryId === categoryId\n );\n if (itemIndex !== -1) {\n setAnchorIndex(model.itemOffsets[itemIndex]);\n scrollToItemRef.current?.(itemIndex, 'start');\n }\n },\n [flattenedItems, model, setAnchorIndex]\n );\n\n const pendingCategoryRef = useRef<string | null>(null);\n\n const handleCategoryClick = useCallback(\n (categoryId: string) => {\n if (isSearching) {\n pendingCategoryRef.current = categoryId;\n setQuery('');\n return;\n }\n scrollToCategory(categoryId);\n },\n [isSearching, setQuery, scrollToCategory]\n );\n\n useEffect(() => {\n if (!isSearching && pendingCategoryRef.current !== null) {\n const categoryId = pendingCategoryRef.current;\n pendingCategoryRef.current = null;\n scrollToCategory(categoryId);\n }\n }, [isSearching, scrollToCategory]);\n\n return (\n <EmojiPickerProvider value={ctxValue}>\n <Box {...getStyles('root')} {...others}>\n {withSearch && (\n <EmojiPickerSearch\n query={query}\n onQueryChange={setQuery}\n placeholder={searchPlaceholder}\n autoFocus={autoFocus}\n inputRef={searchInputRef}\n onKeyDown={handleSearchKeyDown}\n controls={flattenedItems.length > 0 ? gridId : undefined}\n activeDescendant={activeDescendant}\n searchInputProps={searchInputProps}\n />\n )}\n\n {withCategoryNav && (\n <EmojiPickerCategoryNav\n categories={navCategories}\n onCategoryClick={handleCategoryClick}\n />\n )}\n\n <EmojiPickerGrid\n items={flattenedItems}\n itemOffsets={model.itemOffsets}\n columns={cols}\n skinTone={skinTone}\n focusedIndex={keyboardActive ? activeIndex : -1}\n focusedItemIndex={keyboardActive ? getEmojiItemIndex(model, activeIndex) : -1}\n idPrefix={idPrefix}\n gridId={gridId}\n onActiveDescendantChange={setActiveDescendant}\n emojiSize={emojiSize ?? getSizeValues(size).emojiSize}\n emptyContent={emptySearchContent || mergedLabels.noResults}\n onKeyDown={handleGridKeyDown}\n onPointerIndex={setPointerIndex}\n onEmojiClick={handleEmojiClick}\n scrollToItemRef={scrollToItemRef}\n scrollAreaProps={scrollAreaProps}\n />\n\n {(withPreview || withSkinToneSelector) && (\n <EmojiPickerPreview emoji={activeEmoji} skinTone={skinTone} withPreview={!!withPreview}>\n {withSkinToneSelector && (\n <EmojiPickerSkinTone skinTone={skinTone} onSkinToneChange={setSkinTone} />\n )}\n </EmojiPickerPreview>\n )}\n </Box>\n </EmojiPickerProvider>\n );\n});\n\nEmojiPicker.classes = classes;\nEmojiPicker.displayName = '@mantine/emoji/EmojiPicker';\n"],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ "use client";
2
+ // packages/@mantine/emoji/src/components/EmojiPicker/EmojiPicker.module.css
3
+ var EmojiPicker_module_default = {
4
+ "root": "m_873e66bb",
5
+ "search": "m_b2613941",
6
+ "categoryNav": "m_d6a2ef4c",
7
+ "categoryNavButton": "m_f214599e",
8
+ "content": "m_5d2ad400",
9
+ "empty": "m_5fd65a94",
10
+ "categoryLabel": "m_b987dedd",
11
+ "grid": "m_8739711f",
12
+ "emoji": "m_5fd6558d",
13
+ "preview": "m_117c834f",
14
+ "previewEmoji": "m_91abc637",
15
+ "previewName": "m_2e01327a",
16
+ "skinToneDropdown": "m_16864e19",
17
+ "skinToneSelector": "m_774cfde7",
18
+ "skinToneButton": "m_daf4133a"
19
+ };
20
+ export { EmojiPicker_module_default as default };
21
+
22
+ //# sourceMappingURL=EmojiPicker.module.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmojiPicker.module.mjs","names":[],"sources":["../../../src/components/EmojiPicker/EmojiPicker.module.css"],"sourcesContent":[".root {\n --ep-columns: 9;\n --ep-emoji-size: 32px;\n --ep-emoji-fz: 18px;\n --ep-preview-py: 5px;\n --ep-skin-tone-size: 22px;\n --ep-skin-tone-fz: 14px;\n\n display: flex;\n flex-direction: column;\n width: calc(var(--ep-columns) * var(--ep-emoji-size) + var(--mantine-spacing-xs) * 2 + 2px);\n border: 1px solid var(--mantine-color-default-border);\n border-radius: var(--mantine-radius-default);\n background-color: var(--mantine-color-body);\n}\n\n.search {\n padding: var(--mantine-spacing-xs);\n padding-bottom: 0;\n}\n\n.categoryNav {\n display: flex;\n flex-wrap: wrap;\n justify-content: space-between;\n padding: 4px var(--mantine-spacing-xs);\n border-bottom: 1px solid var(--mantine-color-default-border);\n}\n\n.categoryNavButton {\n display: flex;\n align-items: center;\n justify-content: center;\n flex: 1 1 calc(var(--ep-emoji-fz) + 4px);\n max-width: var(--ep-emoji-size);\n height: var(--ep-emoji-size);\n font-size: var(--ep-emoji-fz);\n border-radius: var(--mantine-radius-sm);\n cursor: pointer;\n\n @mixin hover {\n background-color: var(--mantine-color-default-hover);\n }\n}\n\n.content {\n padding: 0 var(--mantine-spacing-xs);\n\n & [role='grid']:focus-visible {\n outline: 2px solid var(--mantine-primary-color-filled);\n outline-offset: -2px;\n }\n}\n\n.empty {\n position: absolute;\n inset: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: var(--mantine-spacing-xs);\n text-align: center;\n}\n\n.categoryLabel {\n display: flex;\n align-items: center;\n font-size: var(--mantine-font-size-xs);\n font-weight: 600;\n color: var(--mantine-color-dimmed);\n padding: var(--mantine-spacing-xs) 0;\n background-color: var(--mantine-color-body);\n}\n\n.grid {\n display: grid;\n grid-template-columns: repeat(var(--ep-columns), var(--ep-emoji-size));\n}\n\n.emoji {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--ep-emoji-size);\n height: var(--ep-emoji-size);\n font-size: var(--ep-emoji-fz);\n border-radius: var(--mantine-radius-sm);\n cursor: pointer;\n line-height: 1;\n padding: 0;\n border: none;\n background: none;\n\n &:focus,\n &:focus-visible {\n outline: none;\n background: none;\n }\n\n &[data-focused] {\n background-color: var(--mantine-color-default-hover);\n outline: 2px solid var(--mantine-primary-color-filled);\n outline-offset: -2px;\n }\n\n @mixin hover {\n background-color: var(--mantine-color-default-hover);\n }\n}\n\n:global(.mantine-focus-auto).emoji:focus-visible {\n outline: none;\n}\n\n.preview {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: var(--mantine-spacing-xs);\n padding: var(--ep-preview-py) var(--mantine-spacing-xs);\n border-top: 1px solid var(--mantine-color-default-border);\n}\n\n.previewEmoji {\n font-size: var(--ep-emoji-fz);\n line-height: 1;\n flex-shrink: 0;\n}\n\n.previewName {\n font-size: var(--mantine-font-size-sm);\n color: var(--mantine-color-dimmed);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n min-width: 0;\n flex: 1;\n}\n\n.skinToneDropdown {\n padding: 4px;\n}\n\n.skinToneSelector {\n display: flex;\n gap: 2px;\n}\n\n.skinToneButton {\n display: flex;\n align-items: center;\n justify-content: center;\n width: var(--ep-skin-tone-size);\n height: var(--ep-skin-tone-size);\n font-size: var(--ep-skin-tone-fz);\n border-radius: var(--mantine-radius-sm);\n cursor: pointer;\n line-height: 1;\n\n @mixin hover {\n background-color: var(--mantine-color-default-hover);\n }\n}\n\n.skinToneSelector .skinToneButton[data-active] {\n background-color: var(--mantine-color-default-hover);\n}\n"],"mappings":""}
@@ -0,0 +1,38 @@
1
+ "use client";
2
+ import { getCanonicalCategoryIcon } from "../../categories.mjs";
3
+ import { useEmojiPickerContext } from "./EmojiPicker.context.mjs";
4
+ import { memo, useState } from "react";
5
+ import { UnstyledButton, createScopedKeydownHandler, useDirection } from "@mantine/core";
6
+ import { jsx } from "react/jsx-runtime";
7
+ // packages/@mantine/emoji/src/components/EmojiPicker/EmojiPickerCategoryNav.tsx
8
+ const EmojiPickerCategoryNav = memo(function EmojiPickerCategoryNav({ categories, onCategoryClick }) {
9
+ const ctx = useEmojiPickerContext();
10
+ const { dir } = useDirection();
11
+ const [tabStopId, setTabStopId] = useState(null);
12
+ const tabStopIndex = Math.max(categories.findIndex((category) => category.id === tabStopId), 0);
13
+ const handleKeyDown = createScopedKeydownHandler({
14
+ siblingSelector: "[data-category-nav-button]",
15
+ parentSelector: "[data-category-nav]",
16
+ orientation: "horizontal",
17
+ loop: true,
18
+ dir
19
+ });
20
+ return /* @__PURE__ */ jsx("div", {
21
+ ...ctx.getStyles("categoryNav"),
22
+ "data-category-nav": true,
23
+ children: categories.map((category, index) => /* @__PURE__ */ jsx(UnstyledButton, {
24
+ ...ctx.getStyles("categoryNavButton"),
25
+ "aria-label": category.label,
26
+ title: category.label,
27
+ tabIndex: index === tabStopIndex ? 0 : -1,
28
+ "data-category-nav-button": true,
29
+ onFocus: () => setTabStopId(category.id),
30
+ onKeyDown: handleKeyDown,
31
+ onClick: () => onCategoryClick(category.id),
32
+ children: getCanonicalCategoryIcon(category.id) || "📁"
33
+ }, category.id))
34
+ });
35
+ });
36
+ export { EmojiPickerCategoryNav };
37
+
38
+ //# sourceMappingURL=EmojiPickerCategoryNav.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmojiPickerCategoryNav.mjs","names":[],"sources":["../../../src/components/EmojiPicker/EmojiPickerCategoryNav.tsx"],"sourcesContent":["import { memo, useState } from 'react';\nimport { createScopedKeydownHandler, UnstyledButton, useDirection } from '@mantine/core';\nimport { getCanonicalCategoryIcon } from '../../categories';\nimport { useEmojiPickerContext } from './EmojiPicker.context';\n\nexport interface EmojiPickerNavCategory {\n id: string;\n label: string;\n}\n\ninterface EmojiPickerCategoryNavProps {\n categories: EmojiPickerNavCategory[];\n onCategoryClick: (categoryId: string) => void;\n}\n\nexport const EmojiPickerCategoryNav = memo(function EmojiPickerCategoryNav({\n categories,\n onCategoryClick,\n}: EmojiPickerCategoryNavProps) {\n const ctx = useEmojiPickerContext();\n const { dir } = useDirection();\n const [tabStopId, setTabStopId] = useState<string | null>(null);\n const tabStopIndex = Math.max(\n categories.findIndex((category) => category.id === tabStopId),\n 0\n );\n\n const handleKeyDown = createScopedKeydownHandler({\n siblingSelector: '[data-category-nav-button]',\n parentSelector: '[data-category-nav]',\n orientation: 'horizontal',\n loop: true,\n dir,\n });\n\n return (\n <div {...ctx.getStyles('categoryNav')} data-category-nav>\n {categories.map((category, index) => (\n <UnstyledButton\n key={category.id}\n {...ctx.getStyles('categoryNavButton')}\n aria-label={category.label}\n title={category.label}\n tabIndex={index === tabStopIndex ? 0 : -1}\n data-category-nav-button\n onFocus={() => setTabStopId(category.id)}\n onKeyDown={handleKeyDown}\n onClick={() => onCategoryClick(category.id)}\n >\n {getCanonicalCategoryIcon(category.id) || '📁'}\n </UnstyledButton>\n ))}\n </div>\n );\n});\n"],"mappings":";;;;;;;AAeA,MAAa,yBAAyB,KAAK,SAAS,uBAAuB,EACzE,YACA,mBAC8B;CAC9B,MAAM,MAAM,sBAAsB;CAClC,MAAM,EAAE,QAAQ,aAAa;CAC7B,MAAM,CAAC,WAAW,gBAAgB,SAAwB,IAAI;CAC9D,MAAM,eAAe,KAAK,IACxB,WAAW,WAAW,aAAa,SAAS,OAAO,SAAS,GAC5D,CACF;CAEA,MAAM,gBAAgB,2BAA2B;EAC/C,iBAAiB;EACjB,gBAAgB;EAChB,aAAa;EACb,MAAM;EACN;CACF,CAAC;CAED,OACE,oBAAC,OAAD;EAAK,GAAI,IAAI,UAAU,aAAa;EAAG,qBAAA;EACpC,UAAA,WAAW,KAAK,UAAU,UACzB,oBAAC,gBAAD;GAEE,GAAI,IAAI,UAAU,mBAAmB;GACrC,cAAY,SAAS;GACrB,OAAO,SAAS;GAChB,UAAU,UAAU,eAAe,IAAI;GACvC,4BAAA;GACA,eAAe,aAAa,SAAS,EAAE;GACvC,WAAW;GACX,eAAe,gBAAgB,SAAS,EAAE;GAEzC,UAAA,yBAAyB,SAAS,EAAE,KAAK;EAC5B,GAXT,SAAS,EAWA,CACjB;CACE,CAAA;AAET,CAAC"}
@@ -0,0 +1,131 @@
1
+ "use client";
2
+ import { useEmojiPickerContext } from "./EmojiPicker.context.mjs";
3
+ import { EmojiButton } from "./EmojiButton.mjs";
4
+ import { useEffect, useRef } from "react";
5
+ import { ScrollArea } from "@mantine/core";
6
+ import { useIsomorphicEffect } from "@mantine/hooks";
7
+ import { jsx } from "react/jsx-runtime";
8
+ import { useVirtualizer } from "@tanstack/react-virtual";
9
+ // packages/@mantine/emoji/src/components/EmojiPicker/EmojiPickerGrid.tsx
10
+ const HEADER_HEIGHT = 32;
11
+ const DEFAULT_SCROLL_AREA_HEIGHT = 300;
12
+ function getEmojiIndexFromEvent(event) {
13
+ const target = event.target.closest("[data-emoji-index]");
14
+ return target ? Number(target.dataset.emojiIndex) : -1;
15
+ }
16
+ function EmojiPickerGrid({ items, itemOffsets, columns, skinTone, focusedIndex, focusedItemIndex, idPrefix, gridId, onActiveDescendantChange, emojiSize, emptyContent, onKeyDown, onPointerIndex, onEmojiClick, scrollToItemRef, scrollAreaProps }) {
17
+ const ctx = useEmojiPickerContext();
18
+ const viewportRef = useRef(null);
19
+ const lastPointerRef = useRef(null);
20
+ const virtualizer = useVirtualizer({
21
+ count: items.length,
22
+ getScrollElement: () => viewportRef.current,
23
+ estimateSize: (index) => items[index].type === "header" ? HEADER_HEIGHT : emojiSize,
24
+ overscan: 10
25
+ });
26
+ useEffect(() => {
27
+ virtualizer.measure();
28
+ }, [emojiSize, virtualizer]);
29
+ useEffect(() => {
30
+ scrollToItemRef.current = (itemIndex, align) => {
31
+ virtualizer.scrollToIndex(itemIndex, { align });
32
+ };
33
+ }, [virtualizer, scrollToItemRef]);
34
+ const virtualItems = virtualizer.getVirtualItems();
35
+ const focusedItemMounted = focusedIndex !== -1 && virtualItems.some((item) => item.index === focusedItemIndex);
36
+ const hasEmojis = items.length > 0;
37
+ const activeDescendant = hasEmojis && focusedItemMounted ? `${idPrefix}-${focusedIndex}` : void 0;
38
+ useIsomorphicEffect(() => {
39
+ onActiveDescendantChange(activeDescendant);
40
+ }, [activeDescendant, onActiveDescendantChange]);
41
+ const handleMouseMove = (event) => {
42
+ const last = lastPointerRef.current;
43
+ if (last && last.x === event.clientX && last.y === event.clientY) return;
44
+ lastPointerRef.current = {
45
+ x: event.clientX,
46
+ y: event.clientY
47
+ };
48
+ const index = getEmojiIndexFromEvent(event);
49
+ if (index !== -1) onPointerIndex(index);
50
+ };
51
+ const handleMouseDown = (event) => {
52
+ if (getEmojiIndexFromEvent(event) !== -1) event.preventDefault();
53
+ };
54
+ const handleClick = (event) => {
55
+ const index = getEmojiIndexFromEvent(event);
56
+ if (index !== -1) onEmojiClick(index);
57
+ };
58
+ return /* @__PURE__ */ jsx(ScrollArea, {
59
+ scrollbars: "y",
60
+ ...scrollAreaProps,
61
+ ...ctx.getStyles("content", { style: {
62
+ height: DEFAULT_SCROLL_AREA_HEIGHT,
63
+ ...scrollAreaProps?.style
64
+ } }),
65
+ viewportRef,
66
+ viewportProps: {
67
+ ...scrollAreaProps?.viewportProps,
68
+ ...hasEmojis ? {
69
+ id: gridId,
70
+ role: "grid",
71
+ tabIndex: 0,
72
+ "aria-label": ctx.labels.emojis,
73
+ "aria-colcount": columns,
74
+ "aria-activedescendant": activeDescendant,
75
+ onKeyDown,
76
+ onMouseMove: handleMouseMove,
77
+ onMouseDown: handleMouseDown,
78
+ onClick: handleClick
79
+ } : { tabIndex: -1 }
80
+ },
81
+ children: hasEmojis ? /* @__PURE__ */ jsx("div", {
82
+ style: {
83
+ height: virtualizer.getTotalSize(),
84
+ width: "100%",
85
+ position: "relative"
86
+ },
87
+ children: virtualItems.map((virtualItem) => {
88
+ const item = items[virtualItem.index];
89
+ const positionStyle = {
90
+ position: "absolute",
91
+ top: 0,
92
+ left: 0,
93
+ width: "100%",
94
+ height: virtualItem.size,
95
+ transform: `translateY(${virtualItem.start}px)`
96
+ };
97
+ if (item.type === "header") return /* @__PURE__ */ jsx("div", {
98
+ role: "row",
99
+ ...ctx.getStyles("categoryLabel", { style: positionStyle }),
100
+ "data-category": item.categoryId,
101
+ children: /* @__PURE__ */ jsx("span", {
102
+ role: "columnheader",
103
+ "aria-colspan": columns,
104
+ children: item.label
105
+ })
106
+ }, `header-${item.categoryId}`);
107
+ const emojiOffset = itemOffsets[virtualItem.index];
108
+ return /* @__PURE__ */ jsx("div", {
109
+ role: "row",
110
+ ...ctx.getStyles("grid", { style: positionStyle }),
111
+ children: item.emojis.map((emoji, colIndex) => {
112
+ const emojiIndex = emojiOffset + colIndex;
113
+ return /* @__PURE__ */ jsx(EmojiButton, {
114
+ emoji,
115
+ skinTone,
116
+ emojiIndex,
117
+ focused: emojiIndex === focusedIndex,
118
+ id: `${idPrefix}-${emojiIndex}`
119
+ }, emoji.id);
120
+ })
121
+ }, `row-${virtualItem.index}`);
122
+ })
123
+ }) : /* @__PURE__ */ jsx("div", {
124
+ ...ctx.getStyles("empty"),
125
+ children: emptyContent
126
+ })
127
+ });
128
+ }
129
+ export { EmojiPickerGrid };
130
+
131
+ //# sourceMappingURL=EmojiPickerGrid.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmojiPickerGrid.mjs","names":[],"sources":["../../../src/components/EmojiPicker/EmojiPickerGrid.tsx"],"sourcesContent":["import { useEffect, useRef } from 'react';\nimport { useVirtualizer } from '@tanstack/react-virtual';\nimport { ScrollArea, ScrollAreaProps } from '@mantine/core';\nimport { useIsomorphicEffect } from '@mantine/hooks';\nimport type { FlattenedRowItem } from '../../types';\nimport { EmojiButton } from './EmojiButton';\nimport { useEmojiPickerContext } from './EmojiPicker.context';\n\nexport type EmojiPickerScrollToItem = (itemIndex: number, align: 'auto' | 'start') => void;\n\ninterface EmojiPickerGridProps {\n items: FlattenedRowItem[];\n itemOffsets: number[];\n columns: number;\n skinTone: number;\n focusedIndex: number;\n focusedItemIndex: number;\n idPrefix: string;\n gridId: string;\n onActiveDescendantChange: (id: string | undefined) => void;\n emojiSize: number;\n emptyContent: React.ReactNode;\n onKeyDown: (event: React.KeyboardEvent<HTMLDivElement>) => void;\n onPointerIndex: (index: number) => void;\n onEmojiClick: (index: number) => void;\n scrollToItemRef: React.RefObject<EmojiPickerScrollToItem | null>;\n scrollAreaProps?: ScrollAreaProps;\n}\n\nconst HEADER_HEIGHT = 32;\nconst DEFAULT_SCROLL_AREA_HEIGHT = 300;\n\nfunction getEmojiIndexFromEvent(event: React.SyntheticEvent) {\n const target = (event.target as HTMLElement).closest<HTMLElement>('[data-emoji-index]');\n return target ? Number(target.dataset.emojiIndex) : -1;\n}\n\nexport function EmojiPickerGrid({\n items,\n itemOffsets,\n columns,\n skinTone,\n focusedIndex,\n focusedItemIndex,\n idPrefix,\n gridId,\n onActiveDescendantChange,\n emojiSize,\n emptyContent,\n onKeyDown,\n onPointerIndex,\n onEmojiClick,\n scrollToItemRef,\n scrollAreaProps,\n}: EmojiPickerGridProps) {\n const ctx = useEmojiPickerContext();\n const viewportRef = useRef<HTMLDivElement>(null);\n const lastPointerRef = useRef<{ x: number; y: number } | null>(null);\n\n const virtualizer = useVirtualizer({\n count: items.length,\n getScrollElement: () => viewportRef.current,\n estimateSize: (index) => (items[index].type === 'header' ? HEADER_HEIGHT : emojiSize),\n overscan: 10,\n });\n\n useEffect(() => {\n virtualizer.measure();\n }, [emojiSize, virtualizer]);\n\n useEffect(() => {\n scrollToItemRef.current = (itemIndex, align) => {\n virtualizer.scrollToIndex(itemIndex, { align });\n };\n }, [virtualizer, scrollToItemRef]);\n\n const virtualItems = virtualizer.getVirtualItems();\n const focusedItemMounted =\n focusedIndex !== -1 && virtualItems.some((item) => item.index === focusedItemIndex);\n const hasEmojis = items.length > 0;\n const activeDescendant =\n hasEmojis && focusedItemMounted ? `${idPrefix}-${focusedIndex}` : undefined;\n\n useIsomorphicEffect(() => {\n onActiveDescendantChange(activeDescendant);\n }, [activeDescendant, onActiveDescendantChange]);\n\n const handleMouseMove = (event: React.MouseEvent<HTMLDivElement>) => {\n const last = lastPointerRef.current;\n if (last && last.x === event.clientX && last.y === event.clientY) {\n return;\n }\n lastPointerRef.current = { x: event.clientX, y: event.clientY };\n const index = getEmojiIndexFromEvent(event);\n if (index !== -1) {\n onPointerIndex(index);\n }\n };\n\n const handleMouseDown = (event: React.MouseEvent<HTMLDivElement>) => {\n if (getEmojiIndexFromEvent(event) !== -1) {\n event.preventDefault();\n }\n };\n\n const handleClick = (event: React.MouseEvent<HTMLDivElement>) => {\n const index = getEmojiIndexFromEvent(event);\n if (index !== -1) {\n onEmojiClick(index);\n }\n };\n\n return (\n <ScrollArea\n scrollbars=\"y\"\n {...scrollAreaProps}\n {...ctx.getStyles('content', {\n style: { height: DEFAULT_SCROLL_AREA_HEIGHT, ...scrollAreaProps?.style },\n })}\n viewportRef={viewportRef}\n viewportProps={{\n ...scrollAreaProps?.viewportProps,\n ...(hasEmojis\n ? {\n id: gridId,\n role: 'grid',\n tabIndex: 0,\n 'aria-label': ctx.labels.emojis,\n 'aria-colcount': columns,\n 'aria-activedescendant': activeDescendant,\n onKeyDown,\n onMouseMove: handleMouseMove,\n onMouseDown: handleMouseDown,\n onClick: handleClick,\n }\n : { tabIndex: -1 }),\n }}\n >\n {hasEmojis ? (\n <div style={{ height: virtualizer.getTotalSize(), width: '100%', position: 'relative' }}>\n {virtualItems.map((virtualItem) => {\n const item = items[virtualItem.index];\n const positionStyle: React.CSSProperties = {\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100%',\n height: virtualItem.size,\n transform: `translateY(${virtualItem.start}px)`,\n };\n\n if (item.type === 'header') {\n return (\n <div\n key={`header-${item.categoryId}`}\n role=\"row\"\n {...ctx.getStyles('categoryLabel', { style: positionStyle })}\n data-category={item.categoryId}\n >\n <span role=\"columnheader\" aria-colspan={columns}>\n {item.label}\n </span>\n </div>\n );\n }\n\n const emojiOffset = itemOffsets[virtualItem.index];\n\n return (\n <div\n key={`row-${virtualItem.index}`}\n role=\"row\"\n {...ctx.getStyles('grid', { style: positionStyle })}\n >\n {item.emojis.map((emoji, colIndex) => {\n const emojiIndex = emojiOffset + colIndex;\n return (\n <EmojiButton\n key={emoji.id}\n emoji={emoji}\n skinTone={skinTone}\n emojiIndex={emojiIndex}\n focused={emojiIndex === focusedIndex}\n id={`${idPrefix}-${emojiIndex}`}\n />\n );\n })}\n </div>\n );\n })}\n </div>\n ) : (\n <div {...ctx.getStyles('empty')}>{emptyContent}</div>\n )}\n </ScrollArea>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ "use client";
2
+ import { applySkinTone } from "../../utils/emoji-grid-utils.mjs";
3
+ import { useEmojiPickerContext } from "./EmojiPicker.context.mjs";
4
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
+ // packages/@mantine/emoji/src/components/EmojiPicker/EmojiPickerPreview.tsx
6
+ function EmojiPickerPreview({ emoji, skinTone, withPreview, children }) {
7
+ const ctx = useEmojiPickerContext();
8
+ const displayEmoji = emoji ? applySkinTone(emoji, skinTone) : null;
9
+ return /* @__PURE__ */ jsxs("div", {
10
+ ...ctx.getStyles("preview"),
11
+ role: withPreview ? "group" : void 0,
12
+ "aria-label": withPreview ? ctx.labels.preview : void 0,
13
+ children: [withPreview && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
14
+ ...ctx.getStyles("previewEmoji"),
15
+ children: displayEmoji?.emoji
16
+ }), /* @__PURE__ */ jsx("span", {
17
+ ...ctx.getStyles("previewName"),
18
+ children: displayEmoji?.name
19
+ })] }), children]
20
+ });
21
+ }
22
+ export { EmojiPickerPreview };
23
+
24
+ //# sourceMappingURL=EmojiPickerPreview.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmojiPickerPreview.mjs","names":[],"sources":["../../../src/components/EmojiPicker/EmojiPickerPreview.tsx"],"sourcesContent":["import type { EmojiPickerEmoji } from '../../types';\nimport { applySkinTone } from '../../utils';\nimport { useEmojiPickerContext } from './EmojiPicker.context';\n\ninterface EmojiPickerPreviewProps {\n emoji: EmojiPickerEmoji | null;\n skinTone: number;\n withPreview: boolean;\n children?: React.ReactNode;\n}\n\nexport function EmojiPickerPreview({\n emoji,\n skinTone,\n withPreview,\n children,\n}: EmojiPickerPreviewProps) {\n const ctx = useEmojiPickerContext();\n const displayEmoji = emoji ? applySkinTone(emoji, skinTone) : null;\n\n return (\n <div\n {...ctx.getStyles('preview')}\n role={withPreview ? 'group' : undefined}\n aria-label={withPreview ? ctx.labels.preview : undefined}\n >\n {withPreview && (\n <>\n <span {...ctx.getStyles('previewEmoji')}>{displayEmoji?.emoji}</span>\n <span {...ctx.getStyles('previewName')}>{displayEmoji?.name}</span>\n </>\n )}\n {children}\n </div>\n );\n}\n"],"mappings":";;;;;AAWA,SAAgB,mBAAmB,EACjC,OACA,UACA,aACA,YAC0B;CAC1B,MAAM,MAAM,sBAAsB;CAClC,MAAM,eAAe,QAAQ,cAAc,OAAO,QAAQ,IAAI;CAE9D,OACE,qBAAC,OAAD;EACE,GAAI,IAAI,UAAU,SAAS;EAC3B,MAAM,cAAc,UAAU,KAAA;EAC9B,cAAY,cAAc,IAAI,OAAO,UAAU,KAAA;EAHjD,UAAA,CAKG,eACC,qBAAA,UAAA,EAAA,UAAA,CACE,oBAAC,QAAD;GAAM,GAAI,IAAI,UAAU,cAAc;GAAI,UAAA,cAAc;EAAY,CAAA,GACpE,oBAAC,QAAD;GAAM,GAAI,IAAI,UAAU,aAAa;GAAI,UAAA,cAAc;EAAW,CAAA,CAClE,EAAA,CAAA,GAEH,QACE;;AAET"}