@atlaskit/emoji 67.0.7 → 67.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/dist/cjs/api/EmojiResource.js +41 -25
- package/dist/cjs/api/media/TokenManager.js +4 -4
- package/dist/cjs/components/common/CachingEmoji.js +14 -6
- package/dist/cjs/components/common/Emoji.js +48 -12
- package/dist/cjs/components/common/EmojiActions.js +62 -26
- package/dist/cjs/components/common/EmojiErrorMessage.js +12 -7
- package/dist/cjs/components/common/EmojiPlaceholder.js +1 -0
- package/dist/cjs/components/common/{EmojiButton.js → EmojiRadioButton.js} +28 -19
- package/dist/cjs/components/common/EmojiUploadPicker.js +80 -37
- package/dist/cjs/components/common/EmojiUploadPreview.js +11 -2
- package/dist/cjs/components/common/FileChooser.js +2 -2
- package/dist/cjs/components/common/ResourcedEmojiComponent.js +4 -0
- package/dist/cjs/components/common/RetryableButton.js +7 -3
- package/dist/cjs/components/common/TonePreviewButton.js +44 -0
- package/dist/cjs/components/common/ToneSelector.js +53 -25
- package/dist/cjs/components/common/styles.js +45 -16
- package/dist/cjs/components/i18n.js +44 -4
- package/dist/cjs/components/picker/CategorySelector.js +112 -90
- package/dist/cjs/components/picker/CategoryTracker.js +0 -28
- package/dist/cjs/components/picker/EmojiPickerCategoryHeading.js +2 -1
- package/dist/cjs/components/picker/EmojiPickerComponent.js +33 -44
- package/dist/cjs/components/picker/EmojiPickerEmojiRow.js +32 -4
- package/dist/cjs/components/picker/EmojiPickerList.js +154 -88
- package/dist/cjs/components/picker/EmojiPickerListSearch.js +66 -117
- package/dist/cjs/components/picker/EmojiPickerVirtualItems.js +5 -2
- package/dist/cjs/components/picker/VirtualList.js +273 -171
- package/dist/cjs/components/picker/styles.js +43 -51
- package/dist/cjs/components/typeahead/EmojiTypeAheadComponent.js +0 -10
- package/dist/cjs/context/EmojiPickerListContext.js +33 -0
- package/dist/cjs/hooks/useEmojiPickerListContext.js +12 -0
- package/dist/cjs/hooks/useIsMounted.js +17 -0
- package/dist/cjs/i18n/cs.js +35 -34
- package/dist/cjs/i18n/da.js +35 -34
- package/dist/cjs/i18n/de.js +35 -34
- package/dist/cjs/i18n/en.js +35 -34
- package/dist/cjs/i18n/en_GB.js +35 -34
- package/dist/cjs/i18n/es.js +35 -34
- package/dist/cjs/i18n/fi.js +35 -34
- package/dist/cjs/i18n/fr.js +35 -34
- package/dist/cjs/i18n/hu.js +35 -34
- package/dist/cjs/i18n/it.js +35 -34
- package/dist/cjs/i18n/ja.js +35 -34
- package/dist/cjs/i18n/ko.js +35 -34
- package/dist/cjs/i18n/nb.js +35 -34
- package/dist/cjs/i18n/nl.js +35 -34
- package/dist/cjs/i18n/pl.js +35 -34
- package/dist/cjs/i18n/pt_BR.js +35 -34
- package/dist/cjs/i18n/ru.js +35 -34
- package/dist/cjs/i18n/sv.js +35 -34
- package/dist/cjs/i18n/th.js +35 -34
- package/dist/cjs/i18n/tr.js +35 -34
- package/dist/cjs/i18n/uk.js +35 -34
- package/dist/cjs/i18n/vi.js +35 -34
- package/dist/cjs/i18n/zh.js +35 -34
- package/dist/cjs/i18n/zh_TW.js +35 -34
- package/dist/cjs/types.js +7 -1
- package/dist/cjs/util/constants.js +43 -2
- package/dist/cjs/util/shared-styles.js +3 -4
- package/dist/cjs/version.json +1 -1
- package/dist/es2019/api/EmojiResource.js +42 -26
- package/dist/es2019/api/media/TokenManager.js +4 -4
- package/dist/es2019/components/common/CachingEmoji.js +10 -3
- package/dist/es2019/components/common/Emoji.js +44 -11
- package/dist/es2019/components/common/EmojiActions.js +55 -24
- package/dist/es2019/components/common/EmojiErrorMessage.js +7 -3
- package/dist/es2019/components/common/EmojiPlaceholder.js +1 -0
- package/dist/es2019/components/common/EmojiRadioButton.js +54 -0
- package/dist/es2019/components/common/EmojiUploadPicker.js +75 -36
- package/dist/es2019/components/common/EmojiUploadPreview.js +11 -2
- package/dist/es2019/components/common/FileChooser.js +1 -1
- package/dist/es2019/components/common/ResourcedEmojiComponent.js +4 -0
- package/dist/es2019/components/common/RetryableButton.js +7 -3
- package/dist/es2019/components/common/TonePreviewButton.js +34 -0
- package/dist/es2019/components/common/ToneSelector.js +55 -21
- package/dist/es2019/components/common/styles.js +41 -10
- package/dist/es2019/components/i18n.js +44 -4
- package/dist/es2019/components/picker/CategorySelector.js +114 -89
- package/dist/es2019/components/picker/CategoryTracker.js +0 -24
- package/dist/es2019/components/picker/EmojiPickerCategoryHeading.js +2 -2
- package/dist/es2019/components/picker/EmojiPickerComponent.js +36 -46
- package/dist/es2019/components/picker/EmojiPickerEmojiRow.js +51 -21
- package/dist/es2019/components/picker/EmojiPickerList.js +113 -55
- package/dist/es2019/components/picker/EmojiPickerListSearch.js +61 -98
- package/dist/es2019/components/picker/EmojiPickerVirtualItems.js +4 -1
- package/dist/es2019/components/picker/VirtualList.js +247 -108
- package/dist/es2019/components/picker/styles.js +20 -28
- package/dist/es2019/components/typeahead/EmojiTypeAheadComponent.js +0 -10
- package/dist/es2019/context/EmojiPickerListContext.js +17 -0
- package/dist/es2019/hooks/useEmojiPickerListContext.js +3 -0
- package/dist/es2019/hooks/useIsMounted.js +11 -0
- package/dist/es2019/i18n/cs.js +35 -34
- package/dist/es2019/i18n/da.js +35 -34
- package/dist/es2019/i18n/de.js +35 -34
- package/dist/es2019/i18n/en.js +35 -34
- package/dist/es2019/i18n/en_GB.js +35 -34
- package/dist/es2019/i18n/es.js +35 -34
- package/dist/es2019/i18n/fi.js +35 -34
- package/dist/es2019/i18n/fr.js +35 -34
- package/dist/es2019/i18n/hu.js +35 -34
- package/dist/es2019/i18n/it.js +35 -34
- package/dist/es2019/i18n/ja.js +35 -34
- package/dist/es2019/i18n/ko.js +35 -34
- package/dist/es2019/i18n/nb.js +35 -34
- package/dist/es2019/i18n/nl.js +35 -34
- package/dist/es2019/i18n/pl.js +35 -34
- package/dist/es2019/i18n/pt_BR.js +35 -34
- package/dist/es2019/i18n/ru.js +35 -34
- package/dist/es2019/i18n/sv.js +35 -34
- package/dist/es2019/i18n/th.js +35 -34
- package/dist/es2019/i18n/tr.js +35 -34
- package/dist/es2019/i18n/uk.js +35 -34
- package/dist/es2019/i18n/vi.js +35 -34
- package/dist/es2019/i18n/zh.js +35 -34
- package/dist/es2019/i18n/zh_TW.js +35 -34
- package/dist/es2019/types.js +5 -0
- package/dist/es2019/util/constants.js +32 -0
- package/dist/es2019/util/shared-styles.js +1 -2
- package/dist/es2019/version.json +1 -1
- package/dist/esm/api/EmojiResource.js +42 -26
- package/dist/esm/api/media/TokenManager.js +4 -4
- package/dist/esm/components/common/CachingEmoji.js +14 -6
- package/dist/esm/components/common/Emoji.js +48 -12
- package/dist/esm/components/common/EmojiActions.js +62 -26
- package/dist/esm/components/common/EmojiErrorMessage.js +7 -3
- package/dist/esm/components/common/EmojiPlaceholder.js +1 -0
- package/dist/esm/components/common/EmojiRadioButton.js +52 -0
- package/dist/esm/components/common/EmojiUploadPicker.js +77 -36
- package/dist/esm/components/common/EmojiUploadPreview.js +11 -2
- package/dist/esm/components/common/FileChooser.js +1 -1
- package/dist/esm/components/common/ResourcedEmojiComponent.js +4 -0
- package/dist/esm/components/common/RetryableButton.js +7 -3
- package/dist/esm/components/common/TonePreviewButton.js +33 -0
- package/dist/esm/components/common/ToneSelector.js +49 -18
- package/dist/esm/components/common/styles.js +40 -12
- package/dist/esm/components/i18n.js +44 -4
- package/dist/esm/components/picker/CategorySelector.js +114 -95
- package/dist/esm/components/picker/CategoryTracker.js +0 -28
- package/dist/esm/components/picker/EmojiPickerCategoryHeading.js +2 -2
- package/dist/esm/components/picker/EmojiPickerComponent.js +35 -46
- package/dist/esm/components/picker/EmojiPickerEmojiRow.js +32 -4
- package/dist/esm/components/picker/EmojiPickerList.js +156 -86
- package/dist/esm/components/picker/EmojiPickerListSearch.js +64 -117
- package/dist/esm/components/picker/EmojiPickerVirtualItems.js +5 -2
- package/dist/esm/components/picker/VirtualList.js +274 -172
- package/dist/esm/components/picker/styles.js +37 -45
- package/dist/esm/components/typeahead/EmojiTypeAheadComponent.js +0 -10
- package/dist/esm/context/EmojiPickerListContext.js +21 -0
- package/dist/esm/hooks/useEmojiPickerListContext.js +5 -0
- package/dist/esm/hooks/useIsMounted.js +11 -0
- package/dist/esm/i18n/cs.js +35 -34
- package/dist/esm/i18n/da.js +35 -34
- package/dist/esm/i18n/de.js +35 -34
- package/dist/esm/i18n/en.js +35 -34
- package/dist/esm/i18n/en_GB.js +35 -34
- package/dist/esm/i18n/es.js +35 -34
- package/dist/esm/i18n/fi.js +35 -34
- package/dist/esm/i18n/fr.js +35 -34
- package/dist/esm/i18n/hu.js +35 -34
- package/dist/esm/i18n/it.js +35 -34
- package/dist/esm/i18n/ja.js +35 -34
- package/dist/esm/i18n/ko.js +35 -34
- package/dist/esm/i18n/nb.js +35 -34
- package/dist/esm/i18n/nl.js +35 -34
- package/dist/esm/i18n/pl.js +35 -34
- package/dist/esm/i18n/pt_BR.js +35 -34
- package/dist/esm/i18n/ru.js +35 -34
- package/dist/esm/i18n/sv.js +35 -34
- package/dist/esm/i18n/th.js +35 -34
- package/dist/esm/i18n/tr.js +35 -34
- package/dist/esm/i18n/uk.js +35 -34
- package/dist/esm/i18n/vi.js +35 -34
- package/dist/esm/i18n/zh.js +35 -34
- package/dist/esm/i18n/zh_TW.js +35 -34
- package/dist/esm/types.js +5 -0
- package/dist/esm/util/constants.js +32 -0
- package/dist/esm/util/shared-styles.js +1 -2
- package/dist/esm/version.json +1 -1
- package/dist/types/api/EmojiResource.d.ts +2 -0
- package/dist/types/components/common/Emoji.d.ts +7 -1
- package/dist/types/components/common/EmojiActions.d.ts +4 -3
- package/dist/types/components/common/{EmojiButton.d.ts → EmojiRadioButton.d.ts} +3 -4
- package/dist/types/components/common/EmojiUploadPicker.d.ts +6 -4
- package/dist/types/components/common/RetryableButton.d.ts +1 -0
- package/dist/types/components/common/TonePreviewButton.d.ts +14 -0
- package/dist/types/components/common/ToneSelector.d.ts +8 -5
- package/dist/types/components/common/internal-types.d.ts +9 -0
- package/dist/types/components/common/styles.d.ts +2 -1
- package/dist/types/components/i18n.d.ts +40 -0
- package/dist/types/components/picker/CategorySelector.d.ts +3 -10
- package/dist/types/components/picker/CategoryTracker.d.ts +0 -2
- package/dist/types/components/picker/EmojiPickerCategoryHeading.d.ts +2 -1
- package/dist/types/components/picker/EmojiPickerEmojiRow.d.ts +5 -0
- package/dist/types/components/picker/EmojiPickerList.d.ts +14 -7
- package/dist/types/components/picker/EmojiPickerListSearch.d.ts +4 -8
- package/dist/types/components/picker/EmojiPickerVirtualItems.d.ts +1 -1
- package/dist/types/components/picker/VirtualList.d.ts +7 -24
- package/dist/types/components/picker/styles.d.ts +1 -1
- package/dist/types/context/EmojiPickerListContext.d.ts +10 -0
- package/dist/types/hooks/useEmojiPickerListContext.d.ts +1 -0
- package/dist/types/hooks/useIsMounted.d.ts +1 -0
- package/dist/types/i18n/cs.d.ts +34 -34
- package/dist/types/i18n/da.d.ts +34 -34
- package/dist/types/i18n/de.d.ts +34 -34
- package/dist/types/i18n/en.d.ts +34 -34
- package/dist/types/i18n/en_GB.d.ts +34 -34
- package/dist/types/i18n/es.d.ts +34 -34
- package/dist/types/i18n/fi.d.ts +34 -34
- package/dist/types/i18n/fr.d.ts +34 -34
- package/dist/types/i18n/hu.d.ts +34 -34
- package/dist/types/i18n/it.d.ts +34 -34
- package/dist/types/i18n/ja.d.ts +34 -34
- package/dist/types/i18n/ko.d.ts +34 -34
- package/dist/types/i18n/nb.d.ts +34 -34
- package/dist/types/i18n/nl.d.ts +34 -34
- package/dist/types/i18n/pl.d.ts +34 -34
- package/dist/types/i18n/pt_BR.d.ts +34 -34
- package/dist/types/i18n/ru.d.ts +34 -34
- package/dist/types/i18n/sv.d.ts +34 -34
- package/dist/types/i18n/th.d.ts +34 -34
- package/dist/types/i18n/tr.d.ts +34 -34
- package/dist/types/i18n/uk.d.ts +34 -34
- package/dist/types/i18n/vi.d.ts +34 -34
- package/dist/types/i18n/zh.d.ts +34 -34
- package/dist/types/i18n/zh_TW.d.ts +34 -34
- package/dist/types/types.d.ts +5 -0
- package/dist/types/util/constants.d.ts +28 -0
- package/dist/types/util/shared-styles.d.ts +1 -1
- package/dist/types/util/type-helpers.d.ts +1 -1
- package/package.json +12 -8
- package/report.api.md +62 -1
- package/README.md +0 -3
- package/dist/cjs/components/hooks.js +0 -14
- package/dist/es2019/components/common/EmojiButton.js +0 -49
- package/dist/es2019/components/hooks.js +0 -8
- package/dist/esm/components/common/EmojiButton.js +0 -43
- package/dist/esm/components/hooks.js +0 -8
- package/dist/types/components/hooks.d.ts +0 -1
|
@@ -1,107 +1,70 @@
|
|
|
1
|
-
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
1
|
/** @jsx jsx */
|
|
3
|
-
import React, {
|
|
2
|
+
import React, { useLayoutEffect, useRef, useState } from 'react';
|
|
4
3
|
import { jsx } from '@emotion/react';
|
|
5
4
|
import TextField from '@atlaskit/textfield';
|
|
6
5
|
import SearchIcon from '@atlaskit/icon/glyph/search';
|
|
7
|
-
import
|
|
6
|
+
import VisuallyHidden from '@atlaskit/visually-hidden';
|
|
7
|
+
import { useIntl } from 'react-intl-next';
|
|
8
8
|
import { messages } from '../i18n';
|
|
9
9
|
import { input, pickerSearch, searchIcon } from './styles';
|
|
10
|
+
import { EMOJI_SEARCH_DEBOUNCE } from '../../util/constants';
|
|
11
|
+
import { useDebouncedCallback } from 'use-debounce';
|
|
10
12
|
export const emojiPickerSearchTestId = 'emoji-picker-serach';
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
export const EmojiPickerListSearch = props => {
|
|
14
|
+
const {
|
|
15
|
+
style,
|
|
16
|
+
query,
|
|
17
|
+
resultsCount,
|
|
18
|
+
onChange
|
|
19
|
+
} = props;
|
|
20
|
+
const textRef = useRef(null);
|
|
21
|
+
const [dirty, setDirty] = useState(false);
|
|
22
|
+
const {
|
|
23
|
+
formatMessage
|
|
24
|
+
} = useIntl();
|
|
25
|
+
|
|
26
|
+
// Debounce callback
|
|
27
|
+
const [debouncedSearch] = useDebouncedCallback(value => {
|
|
28
|
+
onChange(value);
|
|
29
|
+
setDirty(true);
|
|
30
|
+
},
|
|
31
|
+
// delay in ms
|
|
32
|
+
EMOJI_SEARCH_DEBOUNCE);
|
|
33
|
+
const handleOnChange = e => {
|
|
34
|
+
debouncedSearch(e.target.value);
|
|
35
|
+
};
|
|
36
|
+
useLayoutEffect(() => {
|
|
37
|
+
requestAnimationFrame(() => {
|
|
38
|
+
if (textRef) {
|
|
39
|
+
var _textRef$current;
|
|
40
|
+
(_textRef$current = textRef.current) === null || _textRef$current === void 0 ? void 0 : _textRef$current.focus();
|
|
20
41
|
}
|
|
21
42
|
});
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
selectionDirection
|
|
51
|
-
} = this.inputRef;
|
|
52
|
-
if (selectionStart && selectionEnd && selectionDirection) {
|
|
53
|
-
this.inputSelection = {
|
|
54
|
-
selectionStart,
|
|
55
|
-
selectionEnd,
|
|
56
|
-
selectionDirection: selectionDirection
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
restoreInputFocus() {
|
|
62
|
-
this.focusInput();
|
|
63
|
-
if (this.inputSelection && this.inputRef && this.inputRef.setSelectionRange) {
|
|
64
|
-
const {
|
|
65
|
-
selectionStart,
|
|
66
|
-
selectionEnd,
|
|
67
|
-
selectionDirection
|
|
68
|
-
} = this.inputSelection;
|
|
69
|
-
this.inputRef.setSelectionRange(selectionStart, selectionEnd, selectionDirection);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
render() {
|
|
73
|
-
const {
|
|
74
|
-
style,
|
|
75
|
-
query,
|
|
76
|
-
intl
|
|
77
|
-
} = this.props;
|
|
78
|
-
const {
|
|
79
|
-
formatMessage
|
|
80
|
-
} = intl;
|
|
81
|
-
return jsx("div", {
|
|
82
|
-
css: pickerSearch,
|
|
83
|
-
style: style
|
|
84
|
-
}, jsx(TextField, {
|
|
85
|
-
"aria-label": formatMessage(messages.searchLabel),
|
|
86
|
-
css: input,
|
|
87
|
-
autoComplete: "off",
|
|
88
|
-
name: "search",
|
|
89
|
-
placeholder: `${formatMessage(messages.searchPlaceholder)}...`,
|
|
90
|
-
onChange: this.onChange,
|
|
91
|
-
value: query || '',
|
|
92
|
-
ref: this.handleInputRef,
|
|
93
|
-
isCompact: true,
|
|
94
|
-
onBlur: this.onBlur,
|
|
95
|
-
elemBeforeInput: jsx("span", {
|
|
96
|
-
css: searchIcon
|
|
97
|
-
}, jsx(SearchIcon, {
|
|
98
|
-
label: ""
|
|
99
|
-
})),
|
|
100
|
-
testId: emojiPickerSearchTestId
|
|
101
|
-
}));
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
_defineProperty(EmojiPickerListSearch, "defaultProps", {
|
|
105
|
-
style: {}
|
|
106
|
-
});
|
|
107
|
-
export default injectIntl(EmojiPickerListSearch);
|
|
43
|
+
}, []);
|
|
44
|
+
return jsx("div", {
|
|
45
|
+
css: pickerSearch,
|
|
46
|
+
style: style
|
|
47
|
+
}, jsx(VisuallyHidden, {
|
|
48
|
+
id: "emoji-search-results-status",
|
|
49
|
+
role: "status"
|
|
50
|
+
}, dirty && query === '' && formatMessage(messages.searchResultsStatusSeeAll), query !== '' && formatMessage(messages.searchResultsStatus, {
|
|
51
|
+
count: resultsCount
|
|
52
|
+
})), jsx(TextField, {
|
|
53
|
+
role: "searchbox",
|
|
54
|
+
"aria-label": formatMessage(messages.searchLabel),
|
|
55
|
+
css: input,
|
|
56
|
+
autoComplete: "off",
|
|
57
|
+
name: "search",
|
|
58
|
+
placeholder: `${formatMessage(messages.searchPlaceholder)}...`,
|
|
59
|
+
defaultValue: query || '',
|
|
60
|
+
onChange: handleOnChange,
|
|
61
|
+
elemBeforeInput: jsx("span", {
|
|
62
|
+
css: searchIcon
|
|
63
|
+
}, jsx(SearchIcon, {
|
|
64
|
+
label: ""
|
|
65
|
+
})),
|
|
66
|
+
testId: emojiPickerSearchTestId,
|
|
67
|
+
ref: textRef,
|
|
68
|
+
isCompact: true
|
|
69
|
+
}));
|
|
70
|
+
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import _extends from "@babel/runtime/helpers/extends";
|
|
1
2
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
3
|
/** @jsx jsx */
|
|
3
4
|
|
|
@@ -16,7 +17,9 @@ export class AbstractItem {
|
|
|
16
17
|
export class EmojisRowItem extends AbstractItem {
|
|
17
18
|
constructor(props) {
|
|
18
19
|
super(props, sizes.emojiRowHeight);
|
|
19
|
-
_defineProperty(this, "renderItem",
|
|
20
|
+
_defineProperty(this, "renderItem", context => jsx(EmojiPickerEmojiRow, _extends({}, this.props, {
|
|
21
|
+
virtualItemContext: context
|
|
22
|
+
})));
|
|
20
23
|
}
|
|
21
24
|
}
|
|
22
25
|
export class LoadingItem extends AbstractItem {
|
|
@@ -1,42 +1,50 @@
|
|
|
1
|
-
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
1
|
/** @jsx jsx */
|
|
3
2
|
import { jsx } from '@emotion/react';
|
|
4
|
-
import
|
|
5
|
-
import React, { PureComponent, createRef } from 'react';
|
|
3
|
+
import React, { useCallback, useImperativeHandle } from 'react';
|
|
6
4
|
import { virtualList } from './styles';
|
|
5
|
+
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
6
|
+
import { useEmojiPickerListContext } from '../../hooks/useEmojiPickerListContext';
|
|
7
|
+
import { EMOJIPICKERLIST_KEYBOARD_KEYS_SUPPORTED, EMOJI_LIST_COLUMNS, EMOJI_LIST_PAGE_COUNT, KeyboardNavigationDirection, KeyboardKeys } from '../../util/constants';
|
|
7
8
|
export const virtualListScrollContainerTestId = 'virtual-list-scroll-container';
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
9
|
+
export const VirtualList = /*#__PURE__*/React.forwardRef((props, ref) => {
|
|
10
|
+
const parentRef = React.useRef(null);
|
|
11
|
+
const virtualistItemsRef = React.useRef(null);
|
|
12
|
+
const {
|
|
13
|
+
rowRenderer,
|
|
14
|
+
onRowsRendered,
|
|
15
|
+
scrollToAlignment,
|
|
16
|
+
width,
|
|
17
|
+
height,
|
|
18
|
+
rowCount
|
|
19
|
+
} = props;
|
|
20
|
+
const {
|
|
21
|
+
currentEmojisFocus,
|
|
22
|
+
setEmojisFocus
|
|
23
|
+
} = useEmojiPickerListContext();
|
|
24
|
+
const getVirtualizerOptions = () => {
|
|
20
25
|
const {
|
|
21
26
|
rowCount,
|
|
22
27
|
rowHeight,
|
|
23
28
|
overscanRowCount
|
|
24
29
|
} = props;
|
|
25
30
|
return {
|
|
26
|
-
observeElementRect: observeElementRect,
|
|
27
|
-
observeElementOffset: observeElementOffset,
|
|
28
|
-
scrollToFn: elementScroll,
|
|
29
31
|
count: rowCount,
|
|
30
|
-
getScrollElement: () =>
|
|
32
|
+
getScrollElement: () => parentRef.current,
|
|
31
33
|
estimateSize: rowHeight,
|
|
32
34
|
overscan: overscanRowCount,
|
|
33
35
|
onChange: () => {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
const startIndex = getFirstVisibleListElementIndex();
|
|
37
|
+
onRowsRendered({
|
|
38
|
+
startIndex
|
|
39
|
+
});
|
|
40
|
+
},
|
|
41
|
+
scrollPaddingStart: 28,
|
|
42
|
+
scrollPaddingEnd: 28
|
|
36
43
|
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
44
|
+
};
|
|
45
|
+
const rowVirtualizer = useVirtualizer(getVirtualizerOptions());
|
|
46
|
+
const isElementVisible = element => {
|
|
47
|
+
const parent = parentRef.current;
|
|
40
48
|
const elementRect = element.getBoundingClientRect();
|
|
41
49
|
const parentRect = parent.getBoundingClientRect();
|
|
42
50
|
const elemTop = elementRect.top;
|
|
@@ -47,105 +55,236 @@ export class VirtualList extends PureComponent {
|
|
|
47
55
|
// Only completely visible elements return true:
|
|
48
56
|
const isVisible = elemTop >= parentTop && elemBottom <= parentBottom;
|
|
49
57
|
return isVisible;
|
|
50
|
-
}
|
|
51
|
-
getFirstVisibleListElementIndex() {
|
|
52
|
-
var
|
|
53
|
-
const virtualList =
|
|
54
|
-
const renderedElements = (
|
|
58
|
+
};
|
|
59
|
+
const getFirstVisibleListElementIndex = useCallback(() => {
|
|
60
|
+
var _parentRef$current, _parentRef$current$fi;
|
|
61
|
+
const virtualList = rowVirtualizer.getVirtualItems();
|
|
62
|
+
const renderedElements = (_parentRef$current = parentRef.current) === null || _parentRef$current === void 0 ? void 0 : (_parentRef$current$fi = _parentRef$current.firstChild) === null || _parentRef$current$fi === void 0 ? void 0 : _parentRef$current$fi.childNodes;
|
|
55
63
|
if (virtualList.length === 0 || !renderedElements || renderedElements.length === 0) {
|
|
56
64
|
return 0;
|
|
57
65
|
}
|
|
58
|
-
|
|
59
66
|
// Convert NodeListOf<ChildNodes> to ChildNodes[]
|
|
60
67
|
const renderedElementsToArray = Array.from(renderedElements);
|
|
61
|
-
const firstVisibleIndex = renderedElementsToArray.findIndex(elem =>
|
|
68
|
+
const firstVisibleIndex = renderedElementsToArray.findIndex(elem => isElementVisible(elem));
|
|
62
69
|
if (firstVisibleIndex !== -1) {
|
|
63
70
|
var _virtualList$firstVis;
|
|
64
71
|
return ((_virtualList$firstVis = virtualList[firstVisibleIndex]) === null || _virtualList$firstVis === void 0 ? void 0 : _virtualList$firstVis.index) || 0;
|
|
65
72
|
}
|
|
66
73
|
return 0;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
74
|
+
}, [rowVirtualizer]);
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Recurisive function to find next available emoji and it's focus indexes in the grid
|
|
78
|
+
*
|
|
79
|
+
* current focus element is at rowIndex.columnIndex
|
|
80
|
+
* if found element then return the element and focus indexes
|
|
81
|
+
* otherwise change row/column till find the element
|
|
82
|
+
* if can't find the element till reach the edge of grid, we keep current focus states
|
|
83
|
+
*
|
|
84
|
+
* @param rowIndex search from row index (0 based)
|
|
85
|
+
* @param columnIndex search from column index (0 based)
|
|
86
|
+
* @param direction search direction
|
|
87
|
+
*/
|
|
88
|
+
const findNextEmoji = useCallback((rowIndex, columnIndex, direction) => {
|
|
89
|
+
var _virtualistItemsRef$c;
|
|
90
|
+
const emojiToFocus = (_virtualistItemsRef$c = virtualistItemsRef.current) === null || _virtualistItemsRef$c === void 0 ? void 0 : _virtualistItemsRef$c.querySelector(`[data-focus-index="${rowIndex}-${columnIndex}"]`);
|
|
91
|
+
const lastRowIndex = rowCount - 1;
|
|
92
|
+
const lastColumnIndex = EMOJI_LIST_COLUMNS - 1;
|
|
93
|
+
if (emojiToFocus) {
|
|
94
|
+
return {
|
|
95
|
+
element: emojiToFocus,
|
|
96
|
+
rowIndex,
|
|
97
|
+
columnIndex
|
|
98
|
+
};
|
|
86
99
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
100
|
+
switch (direction) {
|
|
101
|
+
case KeyboardNavigationDirection.Down:
|
|
102
|
+
if (rowIndex >= lastRowIndex) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
// find emoji in same column but lower row
|
|
106
|
+
return findNextEmoji(rowIndex + 1, columnIndex, KeyboardNavigationDirection.Down);
|
|
107
|
+
case KeyboardNavigationDirection.Up:
|
|
108
|
+
if (rowIndex <= 0) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
// find emoji in same column but upper row
|
|
112
|
+
return findNextEmoji(rowIndex - 1, columnIndex, KeyboardNavigationDirection.Up);
|
|
113
|
+
case KeyboardNavigationDirection.Left:
|
|
114
|
+
if (rowIndex <= 0) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
if (columnIndex < 0) {
|
|
118
|
+
// find emoji in upper row
|
|
119
|
+
return findNextEmoji(rowIndex - 1, lastColumnIndex, KeyboardNavigationDirection.Left);
|
|
120
|
+
}
|
|
121
|
+
// find emoji on left in the current row
|
|
122
|
+
return findNextEmoji(rowIndex, columnIndex - 1, KeyboardNavigationDirection.Left);
|
|
123
|
+
case KeyboardNavigationDirection.Right:
|
|
124
|
+
if (rowIndex >= lastRowIndex) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
// if no emoji on right, we try first emoji in next row
|
|
128
|
+
return findNextEmoji(rowIndex + 1, 0, KeyboardNavigationDirection.Right);
|
|
129
|
+
default:
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}, [rowCount]);
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Find the valid emoji to scroll and focus
|
|
136
|
+
*/
|
|
137
|
+
const scrollToRowAndFocusEmoji = useCallback(emojiToFocus => {
|
|
138
|
+
if (emojiToFocus) {
|
|
139
|
+
var _emojiToFocus$element;
|
|
140
|
+
rowVirtualizer.scrollToIndex(emojiToFocus.rowIndex, {
|
|
141
|
+
align: 'auto',
|
|
99
142
|
smoothScroll: false
|
|
100
143
|
});
|
|
101
|
-
|
|
144
|
+
(_emojiToFocus$element = emojiToFocus.element) === null || _emojiToFocus$element === void 0 ? void 0 : _emojiToFocus$element.focus({
|
|
145
|
+
preventScroll: true
|
|
146
|
+
});
|
|
147
|
+
setEmojisFocus({
|
|
148
|
+
rowIndex: emojiToFocus.rowIndex,
|
|
149
|
+
columnIndex: emojiToFocus.columnIndex
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}, [rowVirtualizer, setEmojisFocus]);
|
|
153
|
+
const focusEmoji = useCallback((rIndex, cIndex, direction, waitForScrollFinish = false) => {
|
|
154
|
+
if (waitForScrollFinish) {
|
|
155
|
+
// scroll to target rowIndex first to ensure the row is rendered in list.
|
|
156
|
+
// used in page up/down, ctrl+Home, ctrl+End
|
|
157
|
+
rowVirtualizer.scrollToIndex(rIndex, {
|
|
158
|
+
align: 'auto',
|
|
159
|
+
smoothScroll: false
|
|
160
|
+
});
|
|
161
|
+
setTimeout(() => {
|
|
162
|
+
const emojiToFocus = findNextEmoji(rIndex, cIndex, direction);
|
|
163
|
+
scrollToRowAndFocusEmoji(emojiToFocus);
|
|
164
|
+
}, 100); // 100ms is virtual list scrolling time
|
|
165
|
+
} else {
|
|
166
|
+
const emojiToFocus = findNextEmoji(rIndex, cIndex, direction);
|
|
167
|
+
scrollToRowAndFocusEmoji(emojiToFocus);
|
|
102
168
|
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
169
|
+
}, [scrollToRowAndFocusEmoji, findNextEmoji, rowVirtualizer]);
|
|
170
|
+
|
|
171
|
+
// following the guide from https://www.w3.org/WAI/ARIA/apg/patterns/grid/
|
|
172
|
+
const handleKeyDown = e => {
|
|
173
|
+
if (!EMOJIPICKERLIST_KEYBOARD_KEYS_SUPPORTED.includes(e.key)) {
|
|
174
|
+
return;
|
|
107
175
|
}
|
|
108
|
-
}
|
|
109
|
-
recomputeRowHeights() {
|
|
110
|
-
var _this$rowVirtualizer2;
|
|
111
|
-
(_this$rowVirtualizer2 = this.rowVirtualizer) === null || _this$rowVirtualizer2 === void 0 ? void 0 : _this$rowVirtualizer2.measure();
|
|
112
|
-
}
|
|
113
|
-
handleScroll(e) {
|
|
114
176
|
e.preventDefault();
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
177
|
+
const lastRowIndex = rowCount - 1;
|
|
178
|
+
const lastColumnIndex = EMOJI_LIST_COLUMNS - 1;
|
|
179
|
+
|
|
180
|
+
// focus first emoji on first row
|
|
181
|
+
if (e.key === KeyboardKeys.Home && e.ctrlKey) {
|
|
182
|
+
focusEmoji(1, 0, KeyboardNavigationDirection.Up, true);
|
|
183
|
+
return;
|
|
184
|
+
} else if (e.key === KeyboardKeys.End && e.ctrlKey) {
|
|
185
|
+
// focus last available emoji on last row
|
|
186
|
+
focusEmoji(lastRowIndex, lastColumnIndex, KeyboardNavigationDirection.Left, true);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
switch (e.key) {
|
|
190
|
+
// navigate to the right column
|
|
191
|
+
case KeyboardKeys.ArrowRight:
|
|
192
|
+
focusEmoji(currentEmojisFocus.rowIndex, currentEmojisFocus.columnIndex + 1, KeyboardNavigationDirection.Right);
|
|
193
|
+
break;
|
|
194
|
+
// navigate to the left column
|
|
195
|
+
case KeyboardKeys.ArrowLeft:
|
|
196
|
+
focusEmoji(currentEmojisFocus.rowIndex, currentEmojisFocus.columnIndex - 1, KeyboardNavigationDirection.Left);
|
|
197
|
+
break;
|
|
198
|
+
// navigate to the down row
|
|
199
|
+
case KeyboardKeys.ArrowDown:
|
|
200
|
+
focusEmoji(currentEmojisFocus.rowIndex === lastRowIndex ? lastRowIndex : currentEmojisFocus.rowIndex + 1, currentEmojisFocus.columnIndex, KeyboardNavigationDirection.Down);
|
|
201
|
+
break;
|
|
202
|
+
// navigate to the row after {EMOJI_LIST_PAGE_COUNT} rows
|
|
203
|
+
case KeyboardKeys.PageDown:
|
|
204
|
+
focusEmoji(currentEmojisFocus.rowIndex + EMOJI_LIST_PAGE_COUNT, currentEmojisFocus.columnIndex, KeyboardNavigationDirection.Down, true);
|
|
205
|
+
break;
|
|
206
|
+
// navigate to the up row
|
|
207
|
+
case KeyboardKeys.ArrowUp:
|
|
208
|
+
focusEmoji(currentEmojisFocus.rowIndex <= 1 ? 1 : currentEmojisFocus.rowIndex - 1, currentEmojisFocus.columnIndex, KeyboardNavigationDirection.Up);
|
|
209
|
+
break;
|
|
210
|
+
// navigate to the row before {EMOJI_LIST_PAGE_COUNT} rows
|
|
211
|
+
case KeyboardKeys.PageUp:
|
|
212
|
+
focusEmoji(currentEmojisFocus.rowIndex - EMOJI_LIST_PAGE_COUNT, currentEmojisFocus.columnIndex, KeyboardNavigationDirection.Up, true);
|
|
213
|
+
break;
|
|
214
|
+
// navigate to the first cell of current row
|
|
215
|
+
case KeyboardKeys.Home:
|
|
216
|
+
focusEmoji(currentEmojisFocus.rowIndex, 0, KeyboardNavigationDirection.Left);
|
|
217
|
+
break;
|
|
218
|
+
// navigate to the last cell of current row
|
|
219
|
+
case KeyboardKeys.End:
|
|
220
|
+
focusEmoji(currentEmojisFocus.rowIndex, lastColumnIndex, KeyboardNavigationDirection.Left);
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
// Exposing a custom ref handle to the parent component EmojiPickerList to trigger scrollToRow via the listRef
|
|
226
|
+
// https://beta.reactjs.org/reference/react/useImperativeHandle
|
|
227
|
+
useImperativeHandle(ref, () => {
|
|
228
|
+
return {
|
|
229
|
+
scrollToRow(index) {
|
|
230
|
+
if (index !== undefined) {
|
|
231
|
+
rowVirtualizer.setOptions({
|
|
232
|
+
...rowVirtualizer.options,
|
|
233
|
+
scrollPaddingStart: 0
|
|
234
|
+
});
|
|
235
|
+
rowVirtualizer.scrollToIndex(index, {
|
|
236
|
+
align: scrollToAlignment,
|
|
237
|
+
smoothScroll: false
|
|
238
|
+
});
|
|
239
|
+
}
|
|
129
240
|
},
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
left: 0,
|
|
145
|
-
width: '100%',
|
|
146
|
-
height: `${virtualRow.size}px`,
|
|
147
|
-
transform: `translateY(${virtualRow.start}px)`
|
|
241
|
+
scrollToRowAndFocusLastEmoji(index) {
|
|
242
|
+
if (index !== undefined) {
|
|
243
|
+
focusEmoji(index, EMOJI_LIST_COLUMNS, KeyboardNavigationDirection.Left, true);
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
updateFocusIndex(index) {
|
|
247
|
+
var _virtualistItemsRef$c2;
|
|
248
|
+
// row could be removed from virtual list after scrolling, we'll update emoji cell tabIndex after losing focus
|
|
249
|
+
if (!((_virtualistItemsRef$c2 = virtualistItemsRef.current) !== null && _virtualistItemsRef$c2 !== void 0 && _virtualistItemsRef$c2.contains(document.activeElement))) {
|
|
250
|
+
setEmojisFocus({
|
|
251
|
+
rowIndex: index,
|
|
252
|
+
columnIndex: 0
|
|
253
|
+
});
|
|
254
|
+
}
|
|
148
255
|
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
256
|
+
};
|
|
257
|
+
}, [setEmojisFocus, focusEmoji, rowVirtualizer, scrollToAlignment]);
|
|
258
|
+
return jsx("div", {
|
|
259
|
+
ref: parentRef,
|
|
260
|
+
style: {
|
|
261
|
+
height: `${height}px`,
|
|
262
|
+
width: `${width}px`
|
|
263
|
+
},
|
|
264
|
+
css: virtualList,
|
|
265
|
+
"data-testid": virtualListScrollContainerTestId,
|
|
266
|
+
"aria-labelledby": "emoji-picker-table-description",
|
|
267
|
+
role: "grid"
|
|
268
|
+
}, jsx("div", {
|
|
269
|
+
style: {
|
|
270
|
+
height: `${rowVirtualizer.getTotalSize()}px`,
|
|
271
|
+
width: '100%',
|
|
272
|
+
position: 'relative'
|
|
273
|
+
},
|
|
274
|
+
ref: virtualistItemsRef,
|
|
275
|
+
onKeyDown: handleKeyDown,
|
|
276
|
+
role: "presentation"
|
|
277
|
+
}, rowVirtualizer.getVirtualItems().map((virtualRow, index) => jsx("div", {
|
|
278
|
+
key: virtualRow.key,
|
|
279
|
+
style: {
|
|
280
|
+
position: 'absolute',
|
|
281
|
+
top: 0,
|
|
282
|
+
left: 0,
|
|
283
|
+
width: '100%',
|
|
284
|
+
height: `${virtualRow.size}px`,
|
|
285
|
+
transform: `translateY(${virtualRow.start}px)`
|
|
286
|
+
},
|
|
287
|
+
role: "row",
|
|
288
|
+
"aria-rowindex": index + 1
|
|
289
|
+
}, rowRenderer(virtualRow)))));
|
|
290
|
+
});
|