@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.
Files changed (3) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/CLAUDE.md +137 -0
  3. 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.43",
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.34",
45
- "@synerise/ds-divider": "^1.3.15",
46
- "@synerise/ds-dropdown": "^1.3.19",
47
- "@synerise/ds-empty-states": "^1.0.48",
48
- "@synerise/ds-icon": "^1.18.4",
49
- "@synerise/ds-scrollbar": "^1.5.1",
50
- "@synerise/ds-tooltip": "^1.5.3",
51
- "@synerise/ds-utils": "^1.10.1",
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": "a81ab6519d49a3dea9c0cfebcdc9104cbb4f4226"
64
+ "gitHead": "018414e0d7ffd102a07f8aa35fa2e2d3d4ed19e2"
64
65
  }