@synerise/ds-emoji-picker 1.1.5 → 1.2.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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,25 @@
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.2.1](https://github.com/Synerise/synerise-design/compare/@synerise/ds-emoji-picker@1.2.0...@synerise/ds-emoji-picker@1.2.1) (2025-05-26)
7
+
8
+ **Note:** Version bump only for package @synerise/ds-emoji-picker
9
+
10
+
11
+
12
+
13
+
14
+ # [1.2.0](https://github.com/Synerise/synerise-design/compare/@synerise/ds-emoji-picker@1.1.5...@synerise/ds-emoji-picker@1.2.0) (2025-05-19)
15
+
16
+
17
+ ### Features
18
+
19
+ * **emoji-picker:** changed list to virtualised list ([06e6f7a](https://github.com/Synerise/synerise-design/commit/06e6f7ab6f1bbef67adc8662888f35182828b2a4))
20
+
21
+
22
+
23
+
24
+
6
25
  ## [1.1.5](https://github.com/Synerise/synerise-design/compare/@synerise/ds-emoji-picker@1.1.4...@synerise/ds-emoji-picker@1.1.5) (2025-05-07)
7
26
 
8
27
  **Note:** Version bump only for package @synerise/ds-emoji-picker
@@ -1,3 +1,3 @@
1
1
  import React from 'react';
2
- import { EmojiListProps } from './EmojiList.types';
2
+ import type { EmojiListProps } from './EmojiList.types';
3
3
  export declare const EmojiList: ({ texts: customTexts, onSelect, searchQuery }: EmojiListProps) => React.JSX.Element;
@@ -1,14 +1,20 @@
1
- import React, { useMemo } from 'react';
2
- import Divider from '@synerise/ds-divider';
3
- import Scrollbar from '@synerise/ds-scrollbar';
1
+ import React, { useMemo, useRef } from 'react';
4
2
  import { getEmojisByGroup } from 'unicode-emoji-utils';
3
+ import EmptyState, { EmptyStatesSize } from '@synerise/ds-empty-states';
4
+ import Scrollbar from '@synerise/ds-scrollbar';
5
+ import { SearchNoResultsL } from '@synerise/ds-icon';
5
6
  import * as S from './EmojiList.styles';
6
7
  import { useEmojiTranslations } from '../hooks/useEmojiTranslations';
8
+ import { useMultipleItemsPerRow } from '../hooks/useMultipleItemsPerRow';
9
+ import { ITEMS_PER_ROW, ITEM_SIZE } from '../EmojiPicker.const';
10
+ import { createItemData } from './EmojiList.utils';
11
+ import { EmojiListItem } from './EmojiListItem';
7
12
  export var EmojiList = function EmojiList(_ref) {
8
13
  var customTexts = _ref.texts,
9
14
  onSelect = _ref.onSelect,
10
15
  searchQuery = _ref.searchQuery;
11
16
  var texts = useEmojiTranslations(customTexts);
17
+ var listRef = useRef(null);
12
18
  var grouped = useMemo(function () {
13
19
  return getEmojisByGroup('group');
14
20
  }, []);
@@ -30,26 +36,31 @@ export var EmojiList = function EmojiList(_ref) {
30
36
  });
31
37
  return items;
32
38
  }, [searchQuery, grouped, texts]);
33
- var handleClick = function handleClick(emoji) {
34
- // eslint-disable-next-line no-unused-expressions
35
- onSelect == null || onSelect(emoji);
39
+ var virtualListData = useMultipleItemsPerRow(filteredList, ITEMS_PER_ROW);
40
+ var itemData = createItemData(virtualListData, ITEM_SIZE, onSelect, ITEMS_PER_ROW);
41
+ var handleScroll = function handleScroll(_ref2) {
42
+ var currentTarget = _ref2.currentTarget;
43
+ var scrollTop = currentTarget.scrollTop;
44
+ if (listRef.current) {
45
+ listRef.current.scrollTo(scrollTop);
46
+ }
36
47
  };
37
- return /*#__PURE__*/React.createElement(Scrollbar, {
38
- maxHeight: 330
39
- }, filteredList.map(function (_ref2, index) {
40
- var title = _ref2.title,
41
- emojis = _ref2.emojis;
42
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Divider, {
43
- labelBelow: title,
44
- hiddenLine: index === 0
45
- }), /*#__PURE__*/React.createElement(S.EmojiCategoryWrapper, null, emojis.map(function (emoji) {
46
- return /*#__PURE__*/React.createElement(S.EmojiItem, {
47
- key: emoji.emoji,
48
- "data-testid": "ds-emoji-item",
49
- onClick: function onClick() {
50
- return handleClick(emoji);
51
- }
52
- }, emoji.emoji);
53
- })));
48
+ return filteredList.length ? /*#__PURE__*/React.createElement(Scrollbar, {
49
+ maxHeight: 330,
50
+ onScroll: handleScroll
51
+ }, /*#__PURE__*/React.createElement(S.VirtualList, {
52
+ listHeight: 330,
53
+ ref: listRef,
54
+ height: 330,
55
+ itemCount: virtualListData.length,
56
+ itemData: itemData,
57
+ itemSize: ITEM_SIZE,
58
+ width: "100%"
59
+ }, function (props) {
60
+ return /*#__PURE__*/React.createElement(EmojiListItem, props);
61
+ })) : /*#__PURE__*/React.createElement(S.EmptyList, null, /*#__PURE__*/React.createElement(EmptyState, {
62
+ customIcon: /*#__PURE__*/React.createElement(SearchNoResultsL, null),
63
+ size: EmptyStatesSize.SMALL,
64
+ text: texts.empty
54
65
  }));
55
66
  };
@@ -1,3 +1,15 @@
1
1
  import React from 'react';
2
- export declare const EmojiItem: import("styled-components").StyledComponent<(props: any) => React.JSX.Element, any, {}, never>;
2
+ import { FixedSizeList } from 'react-window';
3
+ export declare const EmojiItem: import("styled-components").StyledComponent<"div", any, {
4
+ itemsPerRow: number;
5
+ }, never>;
6
+ export declare const EmojiButton: import("styled-components").StyledComponent<(props: any) => React.JSX.Element, any, {}, never>;
7
+ export declare const EmptyList: import("styled-components").StyledComponent<"div", any, {}, never>;
3
8
  export declare const EmojiCategoryWrapper: import("styled-components").StyledComponent<"div", any, {}, never>;
9
+ export declare const VirtualList: import("styled-components").StyledComponent<typeof FixedSizeList, any, {
10
+ listHeight: number;
11
+ }, never>;
12
+ export declare const ListRow: import("styled-components").StyledComponent<"div", any, {}, never>;
13
+ export declare const Title: import("styled-components").StyledComponent<"div", any, {
14
+ elementSize: string;
15
+ }, never>;
@@ -2,16 +2,47 @@ function _extends() { return _extends = Object.assign ? Object.assign.bind() : f
2
2
  import React from 'react';
3
3
  import styled from 'styled-components';
4
4
  import Button from '@synerise/ds-button';
5
- export var EmojiItem = styled(function (props) {
5
+ import { FixedSizeList } from 'react-window';
6
+ export var EmojiItem = styled.div.withConfig({
7
+ displayName: "EmojiListstyles__EmojiItem",
8
+ componentId: "sc-1yme2ir-0"
9
+ })(["display:flex;width:", "%;align-items:center;justify-content:center;"], function (props) {
10
+ return 100 / props.itemsPerRow;
11
+ });
12
+ export var EmojiButton = styled(function (props) {
6
13
  return /*#__PURE__*/React.createElement(Button, _extends({}, props, {
7
14
  type: "ghost",
8
15
  mode: "single-icon"
9
16
  }));
10
17
  }).withConfig({
11
- displayName: "EmojiListstyles__EmojiItem",
12
- componentId: "sc-1yme2ir-0"
13
- })(["font-size:18px;display:flex;align-items:center;cursor:pointer;"]);
18
+ displayName: "EmojiListstyles__EmojiButton",
19
+ componentId: "sc-1yme2ir-1"
20
+ })(["font-size:24px;display:flex;align-items:center;cursor:pointer;"]);
21
+ export var EmptyList = styled.div.withConfig({
22
+ displayName: "EmojiListstyles__EmptyList",
23
+ componentId: "sc-1yme2ir-2"
24
+ })(["padding:12px;"]);
14
25
  export var EmojiCategoryWrapper = styled.div.withConfig({
15
26
  displayName: "EmojiListstyles__EmojiCategoryWrapper",
16
- componentId: "sc-1yme2ir-1"
17
- })(["display:flex;gap:4px;flex-wrap:wrap;"]);
27
+ componentId: "sc-1yme2ir-3"
28
+ })(["display:flex;gap:4px;flex-wrap:wrap;"]);
29
+ export var VirtualList = styled(FixedSizeList).withConfig({
30
+ displayName: "EmojiListstyles__VirtualList",
31
+ componentId: "sc-1yme2ir-4"
32
+ })(["max-height:", "px;height:auto !important;overflow-x:unset !important;overflow-y:unset !important;"], function (props) {
33
+ return props.listHeight;
34
+ });
35
+ export var ListRow = styled.div.withConfig({
36
+ displayName: "EmojiListstyles__ListRow",
37
+ componentId: "sc-1yme2ir-5"
38
+ })(["display:flex;flex-wrap:wrap;justify-content:flex-start;align-items:center;box-sizing:border-box;width:100%;height:100%;max-width:100%;"]);
39
+ export var Title = styled.div.withConfig({
40
+ displayName: "EmojiListstyles__Title",
41
+ componentId: "sc-1yme2ir-6"
42
+ })(["font-size:10px;line-height:1.6;font-weight:500;text-transform:uppercase;color:", ";padding:0 12px;flex-basis:100%;height:", ";line-height:", ";"], function (props) {
43
+ return props.theme.palette['grey-500'];
44
+ }, function (props) {
45
+ return props.elementSize;
46
+ }, function (props) {
47
+ return props.elementSize;
48
+ });
@@ -1,4 +1,4 @@
1
- import type { ReactNode } from 'react';
1
+ import type { CSSProperties, ReactNode } from 'react';
2
2
  import type { Emoji } from 'unicode-emoji-utils';
3
3
  export type EmojiListTexts = {
4
4
  'smileys-emotion': ReactNode;
@@ -20,3 +20,24 @@ export type EmojiCategory = {
20
20
  title: ReactNode;
21
21
  emojis: Emoji[];
22
22
  };
23
+ export type RowItemProps = {
24
+ data: ListItemData;
25
+ index: number;
26
+ style: CSSProperties;
27
+ };
28
+ export type TitleItem = {
29
+ title: ReactNode;
30
+ };
31
+ export type EmojiVirtualListItem = Emoji[] | TitleItem[];
32
+ export type ListItemData = {
33
+ items: EmojiVirtualListItem[];
34
+ onSelect?: (emoji: Emoji) => void;
35
+ itemsPerRow: number;
36
+ elementSize: number;
37
+ };
38
+ export type ListItemProps = {
39
+ element: Emoji;
40
+ index: number;
41
+ itemsPerRow: number;
42
+ onSelect: (emoji: Emoji) => void;
43
+ };
@@ -0,0 +1,2 @@
1
+ import type { ListItemData } from './EmojiList.types';
2
+ export declare const createItemData: (items: ListItemData['items'], elementSize: ListItemData['elementSize'], onSelect: ListItemData['onSelect'], itemsPerRow: ListItemData['itemsPerRow']) => ListItemData;
@@ -0,0 +1,9 @@
1
+ import memoize from 'memoize-one';
2
+ export var createItemData = memoize(function (items, elementSize, onSelect, itemsPerRow) {
3
+ return {
4
+ items: items,
5
+ elementSize: elementSize,
6
+ onSelect: onSelect,
7
+ itemsPerRow: itemsPerRow
8
+ };
9
+ });
@@ -0,0 +1,3 @@
1
+ import React, { ReactElement } from 'react';
2
+ import { RowItemProps } from './EmojiList.types';
3
+ export declare const EmojiListItem: React.MemoExoticComponent<({ data, index, style }: RowItemProps) => ReactElement>;
@@ -0,0 +1,31 @@
1
+ import React, { memo } from 'react';
2
+ import { areEqual } from 'react-window';
3
+ import * as S from './EmojiList.styles';
4
+ var isTitle = function isTitle(item) {
5
+ return item[0] && 'title' in item[0];
6
+ };
7
+ export var EmojiListItem = /*#__PURE__*/memo(function (_ref) {
8
+ var data = _ref.data,
9
+ index = _ref.index,
10
+ style = _ref.style;
11
+ var elementSize = data.elementSize,
12
+ itemsPerRow = data.itemsPerRow,
13
+ onSelect = data.onSelect,
14
+ items = data.items;
15
+ var rowItems = items[index];
16
+ return /*#__PURE__*/React.createElement("div", {
17
+ style: style
18
+ }, /*#__PURE__*/React.createElement(S.ListRow, null, isTitle(rowItems) ? /*#__PURE__*/React.createElement(S.Title, {
19
+ elementSize: elementSize + "px"
20
+ }, rowItems[0].title) : rowItems.map(function (item) {
21
+ return /*#__PURE__*/React.createElement(S.EmojiItem, {
22
+ onClick: function onClick() {
23
+ return onSelect && onSelect(item);
24
+ },
25
+ itemsPerRow: itemsPerRow,
26
+ key: item.emoji
27
+ }, /*#__PURE__*/React.createElement(S.EmojiButton, {
28
+ "data-testid": "ds-emoji-item"
29
+ }, item.emoji));
30
+ })));
31
+ }, areEqual);
@@ -0,0 +1,2 @@
1
+ export declare const ITEMS_PER_ROW = 6;
2
+ export declare const ITEM_SIZE = 32;
@@ -0,0 +1,2 @@
1
+ export var ITEMS_PER_ROW = 6;
2
+ export var ITEM_SIZE = 32;
@@ -31,7 +31,10 @@ export var EmojiPicker = function EmojiPicker(_ref) {
31
31
  placement: "bottomRight"
32
32
  }, dropdownProps, {
33
33
  overlay: /*#__PURE__*/React.createElement(S.Overlay, {
34
- ref: ref
34
+ ref: ref,
35
+ onClick: function onClick(event) {
36
+ return event.stopPropagation();
37
+ }
35
38
  }, /*#__PURE__*/React.createElement(EmojiOverlay, {
36
39
  onSelect: function onSelect(val) {
37
40
  closeOnSelect && toggleOpen(false);
@@ -1,9 +1,10 @@
1
- import type { ReactElement } from 'react';
1
+ import type { ReactElement, ReactNode } from 'react';
2
2
  import type { DropdownProps } from '@synerise/ds-dropdown';
3
3
  import type { Emoji } from 'unicode-emoji-utils';
4
4
  import type { EmojiListTexts } from './EmojiList/EmojiList.types';
5
5
  export type EmojiPickerTexts = EmojiListTexts & {
6
6
  placeholder: string;
7
+ empty: ReactNode;
7
8
  };
8
9
  export type EmojiPickerProps = {
9
10
  children?: ReactElement;
@@ -45,6 +45,10 @@ export var useEmojiTranslations = function useEmojiTranslations(texts) {
45
45
  placeholder: formatMessage({
46
46
  id: 'DS.ICON-PICKER.PLACEHOLDER',
47
47
  defaultMessage: 'Search'
48
+ }),
49
+ empty: /*#__PURE__*/React.createElement(FormattedMessage, {
50
+ id: "DS.ICON-PICKER.NO-MATCHES",
51
+ defaultMessage: "No emojis found"
48
52
  })
49
53
  }, texts || {});
50
54
  }, [texts, formatMessage]);
@@ -0,0 +1,2 @@
1
+ import type { EmojiVirtualListItem, EmojiCategory } from '../EmojiList/EmojiList.types';
2
+ export declare const useMultipleItemsPerRow: (data: EmojiCategory[], itemsPerRow: number) => EmojiVirtualListItem[];
@@ -0,0 +1,26 @@
1
+ import { useMemo } from 'react';
2
+ export var useMultipleItemsPerRow = function useMultipleItemsPerRow(data, itemsPerRow) {
3
+ var groupedData = useMemo(function () {
4
+ return data.reduce(function (flatItemsAndCategories, group) {
5
+ var title = group.title,
6
+ emojis = group.emojis;
7
+ flatItemsAndCategories.push([{
8
+ title: title
9
+ }]);
10
+ var groupItemsLength = emojis.length;
11
+ var rowCount = Math.ceil(groupItemsLength / itemsPerRow);
12
+ for (var i = 0; i < rowCount; i += 1) {
13
+ var rowItems = [];
14
+ for (var j = 0; j < itemsPerRow; j += 1) {
15
+ var idx = itemsPerRow * i + j;
16
+ if (idx < groupItemsLength) {
17
+ rowItems.push(emojis[idx]);
18
+ }
19
+ }
20
+ flatItemsAndCategories.push(rowItems);
21
+ }
22
+ return flatItemsAndCategories;
23
+ }, []);
24
+ }, [data, itemsPerRow]);
25
+ return groupedData;
26
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synerise/ds-emoji-picker",
3
- "version": "1.1.5",
3
+ "version": "1.2.1",
4
4
  "description": "EmojiPicker UI Component for the Synerise Design System",
5
5
  "license": "ISC",
6
6
  "repository": "Synerise/synerise-design",
@@ -34,12 +34,12 @@
34
34
  ],
35
35
  "types": "dist/index.d.ts",
36
36
  "dependencies": {
37
- "@synerise/ds-button": "^1.2.0",
38
- "@synerise/ds-divider": "^1.0.7",
39
- "@synerise/ds-dropdown": "^1.0.7",
40
- "@synerise/ds-icon": "^1.4.2",
41
- "@synerise/ds-scrollbar": "^1.0.7",
42
- "@synerise/ds-utils": "^1.1.0",
37
+ "@synerise/ds-button": "^1.3.1",
38
+ "@synerise/ds-divider": "^1.0.9",
39
+ "@synerise/ds-dropdown": "^1.0.9",
40
+ "@synerise/ds-icon": "^1.5.1",
41
+ "@synerise/ds-scrollbar": "^1.0.9",
42
+ "@synerise/ds-utils": "^1.1.1",
43
43
  "unicode-emoji-utils": "^1.2.0"
44
44
  },
45
45
  "peerDependencies": {
@@ -48,5 +48,5 @@
48
48
  "react-intl": ">=3.12.0 <= 6.8",
49
49
  "styled-components": "^5.3.3"
50
50
  },
51
- "gitHead": "90af7549cf0f556340bdd0deec4bb59bacf3e95e"
51
+ "gitHead": "9e9cc54e3690b4d51323f91c81b3b79305984da6"
52
52
  }