@synerise/ds-emoji-picker 1.3.43 → 1.3.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/CLAUDE.md +137 -0
- package/package.json +11 -10
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [1.3.45](https://github.com/Synerise/synerise-design/compare/@synerise/ds-emoji-picker@1.3.44...@synerise/ds-emoji-picker@1.3.45) (2026-07-24)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @synerise/ds-emoji-picker
|
|
9
|
+
|
|
10
|
+
## [1.3.44](https://github.com/Synerise/synerise-design/compare/@synerise/ds-emoji-picker@1.3.43...@synerise/ds-emoji-picker@1.3.44) (2026-07-23)
|
|
11
|
+
|
|
12
|
+
**Note:** Version bump only for package @synerise/ds-emoji-picker
|
|
13
|
+
|
|
6
14
|
## [1.3.43](https://github.com/Synerise/synerise-design/compare/@synerise/ds-emoji-picker@1.3.42...@synerise/ds-emoji-picker@1.3.43) (2026-07-16)
|
|
7
15
|
|
|
8
16
|
**Note:** Version bump only for package @synerise/ds-emoji-picker
|
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# EmojiPicker (`@synerise/ds-emoji-picker`)
|
|
2
|
+
|
|
3
|
+
> A dropdown trigger + panel that renders a searchable, virtualised grid of Unicode emojis grouped by category. The caller supplies any element as the trigger via `children`.
|
|
4
|
+
|
|
5
|
+
## Package structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
src/
|
|
9
|
+
EmojiPicker.tsx — root component; manages open/focus state, renders ds-dropdown
|
|
10
|
+
EmojiPicker.types.ts — EmojiPickerProps, EmojiPickerTexts
|
|
11
|
+
EmojiPicker.styles.ts — minimal: just the Overlay wrapper div
|
|
12
|
+
EmojiPicker.const.ts — ITEMS_PER_ROW (6), ITEM_SIZE (32px)
|
|
13
|
+
index.ts — public exports
|
|
14
|
+
EmojiOverlay/
|
|
15
|
+
EmojiOverlay.tsx — search input (Dropdown.SearchInput) + EmojiList
|
|
16
|
+
EmojiOverlay.types.ts — EmojiOverlayType (internal)
|
|
17
|
+
EmojiList/
|
|
18
|
+
EmojiList.tsx — virtualised list with Scrollbar wrapper
|
|
19
|
+
EmojiList.types.ts — all internal list types
|
|
20
|
+
EmojiList.styles.tsx — styled-components for list items, buttons, titles
|
|
21
|
+
EmojiList.utils.tsx — createItemData (memoized), getEmojisByGroup wrapper
|
|
22
|
+
EmojiListItem.tsx — memo'd row renderer for react-window
|
|
23
|
+
hooks/
|
|
24
|
+
useEmojiTranslations.tsx — merges custom texts with react-intl defaults
|
|
25
|
+
useMultipleItemsPerRow.ts — flattens category groups into virtual list rows
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Public exports
|
|
29
|
+
|
|
30
|
+
### `EmojiPicker` (named + default)
|
|
31
|
+
|
|
32
|
+
| Prop | Type | Default | Description |
|
|
33
|
+
|------|------|---------|-------------|
|
|
34
|
+
| `children` | `ReactElement` | `undefined` | Trigger element. Single React element (not `ReactNode`). |
|
|
35
|
+
| `onSelect` | `(emoji: Emoji) => void` | `undefined` | Called when an emoji is clicked. `Emoji` is from `unicode-emoji-utils`. |
|
|
36
|
+
| `closeOnSelect` | `boolean` | `true` | Whether to close the dropdown after an emoji is selected. |
|
|
37
|
+
| `dropdownProps` | `Partial<Omit<DropdownSharedProps, 'children' \| 'size' \| 'overlay' \| 'onOpenChage' \| 'open'>>` | `undefined` | Passes through to `@synerise/ds-dropdown`. `size`, `overlay`, and `open` are controlled internally. |
|
|
38
|
+
| `texts` | `Partial<EmojiPickerTexts>` | `undefined` | Override any label/placeholder text (see `EmojiPickerTexts` below). |
|
|
39
|
+
|
|
40
|
+
### `EmojiPickerTexts`
|
|
41
|
+
|
|
42
|
+
All fields are `ReactNode`. All are optional when passed via `texts` prop.
|
|
43
|
+
|
|
44
|
+
| Key | Default (en) |
|
|
45
|
+
|-----|-------------|
|
|
46
|
+
| `placeholder` | `"Search"` |
|
|
47
|
+
| `empty` | `"No emojis found"` |
|
|
48
|
+
| `smileys-emotion` | `"Smileys"` |
|
|
49
|
+
| `people-body` | `"People & body"` |
|
|
50
|
+
| `animals-nature` | `"Animals & nature"` |
|
|
51
|
+
| `food-drink` | `"Food & drink"` |
|
|
52
|
+
| `travel-places` | `"Travel & places"` |
|
|
53
|
+
| `activities` | `"Activities"` |
|
|
54
|
+
| `objects` | `"Objects"` |
|
|
55
|
+
| `symbols` | `"Symbols"` |
|
|
56
|
+
| `flags` | `"Flags"` |
|
|
57
|
+
|
|
58
|
+
### `EmojiPickerProps`
|
|
59
|
+
|
|
60
|
+
Type exported from `index.ts`.
|
|
61
|
+
|
|
62
|
+
## Usage patterns
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
import EmojiPicker from '@synerise/ds-emoji-picker';
|
|
66
|
+
import type { Emoji } from 'unicode-emoji-utils';
|
|
67
|
+
|
|
68
|
+
// Minimal — any element as trigger
|
|
69
|
+
<EmojiPicker onSelect={(emoji: Emoji) => console.log(emoji.emoji)}>
|
|
70
|
+
<Button>Pick emoji</Button>
|
|
71
|
+
</EmojiPicker>
|
|
72
|
+
|
|
73
|
+
// With custom texts and keep-open-on-select
|
|
74
|
+
<EmojiPicker
|
|
75
|
+
onSelect={handleSelect}
|
|
76
|
+
closeOnSelect={false}
|
|
77
|
+
texts={{ placeholder: 'Search emoji..', empty: 'Nothing found' }}
|
|
78
|
+
>
|
|
79
|
+
<Icon component={<EmoticonsM />} />
|
|
80
|
+
</EmojiPicker>
|
|
81
|
+
|
|
82
|
+
// Append emoji to an input field
|
|
83
|
+
const [value, setValue] = useState('');
|
|
84
|
+
<Input
|
|
85
|
+
value={value}
|
|
86
|
+
icon1={
|
|
87
|
+
<EmojiPicker closeOnSelect={false} onSelect={(e) => setValue(v => v + e.emoji)}>
|
|
88
|
+
<Icon component={<EmoticonsM />} />
|
|
89
|
+
</EmojiPicker>
|
|
90
|
+
}
|
|
91
|
+
/>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Architecture
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
EmojiPicker (manages isOpen + focus)
|
|
98
|
+
└── ds-dropdown (open, placement="bottomRight", asChild)
|
|
99
|
+
overlay:
|
|
100
|
+
S.Overlay (stopPropagation on click)
|
|
101
|
+
└── EmojiOverlay
|
|
102
|
+
├── Dropdown.SearchInput (autofocus when opened)
|
|
103
|
+
└── EmojiList
|
|
104
|
+
└── ds-scrollbar (maxHeight=330)
|
|
105
|
+
└── VirtualList (react-window FixedSizeList)
|
|
106
|
+
└── EmojiListItem (memo + areEqual)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Custom hooks
|
|
110
|
+
|
|
111
|
+
### `useEmojiTranslations`
|
|
112
|
+
|
|
113
|
+
Merges the consumer's partial `texts` override with `react-intl` `FormattedMessage` defaults. **Requires an `IntlProvider` in the React tree.** Returns a complete `EmojiPickerTexts` object.
|
|
114
|
+
|
|
115
|
+
### `useMultipleItemsPerRow`
|
|
116
|
+
|
|
117
|
+
Takes `EmojiCategory[]` and `itemsPerRow` (6), returns a flat `EmojiVirtualListItem[]` array where each element is either a `[TitleItem]` (category header) or `Emoji[]` (one row of up to 6 emojis). This is the data shape consumed by react-window.
|
|
118
|
+
|
|
119
|
+
## Key dependencies
|
|
120
|
+
|
|
121
|
+
- `unicode-emoji-utils` — provides `getEmojisByGroup('group')` which returns a `Map<category, Emoji[]>` with keyword metadata used for search
|
|
122
|
+
- `react-window` (`FixedSizeList`) — virtual scroll for the emoji grid (item height = 32px, up to 330px visible)
|
|
123
|
+
- `@synerise/ds-dropdown` — the popover/dropdown shell
|
|
124
|
+
- `@synerise/ds-scrollbar` — wraps the virtual list to intercept scroll events and forward them to `listRef.current.scrollTo()`
|
|
125
|
+
- `memoize-one` — memoizes `createItemData` to prevent react-window item re-renders
|
|
126
|
+
- `react-intl` — **peer dependency** — `useEmojiTranslations` calls `useIntl()` and `FormattedMessage`; an `IntlProvider` must be present
|
|
127
|
+
|
|
128
|
+
## Implementation notes
|
|
129
|
+
|
|
130
|
+
- **`react-intl` is required** — there is no fallback if `IntlProvider` is absent. Tests use `renderWithProvider` from `@synerise/ds-core` which wraps the tree with `IntlProvider`.
|
|
131
|
+
- **Click-outside handling** — `useOnClickOutside` is used with a special guard: if the click target is inside `triggerRef`, the handler returns early to avoid fighting with the dropdown's own toggle logic.
|
|
132
|
+
- **Overlay stops propagation** — the `S.Overlay` div has `onClick={(event) => event.stopPropagation()}`, preventing emoji clicks from bubbling to parent handlers (e.g. form or modal click-outside listeners).
|
|
133
|
+
- **`children` must be a single `ReactElement`** (typed as `ReactElement`, not `ReactNode`) — the dropdown's `asChild` mode merges the open handler onto the child. Passing a fragment or string will break it.
|
|
134
|
+
- **`dropdownProps` typo** — the omit list includes `'onOpenChage'` (missing `n`). The actual Dropdown prop is `onOpenChange`. This means `onOpenChange` is not actually omitted from the type, though it is overridden internally.
|
|
135
|
+
- **All `FormattedMessage` category IDs are identical** (`DS.ICON-PICKER.SMILEYS`) — i18n overrides via message IDs will affect all categories at once. Use the `texts` prop to override categories individually.
|
|
136
|
+
- **Scrollbar scroll sync** — `Scrollbar` fires a `onScroll` event handler that manually calls `listRef.current.scrollTo(scrollTop)` because the virtual list's own scroll is disabled (`overflow: unset`) so that `ds-scrollbar`'s custom scrollbar renders correctly.
|
|
137
|
+
- **Uses Vitest** for testing.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synerise/ds-emoji-picker",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.45",
|
|
4
4
|
"description": "EmojiPicker UI Component for the Synerise Design System",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"repository": "Synerise/synerise-design",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"files": [
|
|
18
18
|
"/dist",
|
|
19
19
|
"CHANGELOG.md",
|
|
20
|
+
"CLAUDE.md",
|
|
20
21
|
"README.md",
|
|
21
22
|
"package.json",
|
|
22
23
|
"LICENSE.md"
|
|
@@ -41,14 +42,14 @@
|
|
|
41
42
|
],
|
|
42
43
|
"types": "dist/index.d.ts",
|
|
43
44
|
"dependencies": {
|
|
44
|
-
"@synerise/ds-button": "^1.5.
|
|
45
|
-
"@synerise/ds-divider": "^1.3.
|
|
46
|
-
"@synerise/ds-dropdown": "^1.3.
|
|
47
|
-
"@synerise/ds-empty-states": "^1.0.
|
|
48
|
-
"@synerise/ds-icon": "^1.18.
|
|
49
|
-
"@synerise/ds-scrollbar": "^1.5.
|
|
50
|
-
"@synerise/ds-tooltip": "^1.5.
|
|
51
|
-
"@synerise/ds-utils": "^1.10.
|
|
45
|
+
"@synerise/ds-button": "^1.5.35",
|
|
46
|
+
"@synerise/ds-divider": "^1.3.16",
|
|
47
|
+
"@synerise/ds-dropdown": "^1.3.21",
|
|
48
|
+
"@synerise/ds-empty-states": "^1.0.49",
|
|
49
|
+
"@synerise/ds-icon": "^1.18.5",
|
|
50
|
+
"@synerise/ds-scrollbar": "^1.5.2",
|
|
51
|
+
"@synerise/ds-tooltip": "^1.5.4",
|
|
52
|
+
"@synerise/ds-utils": "^1.10.2",
|
|
52
53
|
"memoize-one": "^5.1.1",
|
|
53
54
|
"react-window": "^1.8.11",
|
|
54
55
|
"unicode-emoji-utils": "^1.2.0"
|
|
@@ -60,5 +61,5 @@
|
|
|
60
61
|
"styled-components": "^5.3.3",
|
|
61
62
|
"vitest": "4"
|
|
62
63
|
},
|
|
63
|
-
"gitHead": "
|
|
64
|
+
"gitHead": "018414e0d7ffd102a07f8aa35fa2e2d3d4ed19e2"
|
|
64
65
|
}
|