-
Notifications
You must be signed in to change notification settings - Fork 272
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[WIP] Add new EmojiPicker, adjust message inputs
- Loading branch information
1 parent
22aff77
commit cf71aff
Showing
8 changed files
with
256 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
--- | ||
id: new-emoji-picker-integration | ||
sidebar_position: 3 | ||
title: EmojiPicker Integration (11.0.0) | ||
keywords: [migration guide, upgrade, emoji picker, breaking changes, v11] | ||
--- | ||
|
||
import GHComponentLink from '../_docusaurus-components/GHComponentLink'; | ||
|
||
## Dropping support for built-in `EmojiPicker` (with breaking changes) | ||
|
||
By default - our SDK would ship with `emoji-mart` dependency on top of which our `EmojiPicker` component is built. And since the SDK is using `emoji-mart` for this component - it was also reused for reactions (`ReactionsList` and `ReactionSelector`) and suggestion list (`MessageInput`). This solution proved to be very uncomfortable to work with when it came to replacing either of the mentioned components (or disabling them completely) and the final applications using our SDK would still bundle certain `emoji-mart` parts which weren't needed (or seemingly "disabled") resulting in sub-optimal load times. Maintaining such architecture became a burden so we're switching things a bit. | ||
|
||
## Changes to the default component composition (architecture) | ||
|
||
Component now comes as two-part "bundle" - a button and an actual picker element. The component now holds its own `open` state which is handled by the button. | ||
|
||
{/* TODO: extend once you have the component ready */} | ||
{/* TODO: mention that we're dropping the EmojiContext */} | ||
|
||
## Switching to opt-in mechanism (BREAKING CHANGE) | ||
|
||
We made `emoji-mart` package in our SDK completely optional which means that `EmojiPicker` component is now disabled by default. | ||
|
||
### Reinstate the `EmojiPicker` component | ||
|
||
Add `emoji-mart` to your packages and make sure the package versions fit our peer-dependency requirements: | ||
|
||
```bash | ||
yarn add emoji-mart@^5.5.2 @emoji-mart/data@^1.1.2 @emoji-mart/react@^1.1.1 | ||
``` | ||
|
||
\\Import `EmojiPicker` component from the `stream-chat-react` package: | ||
|
||
```tsx | ||
import { Channel, EmojiPicker } from 'stream-chat-react'; | ||
|
||
// and apply it to the Channel (component context) | ||
|
||
const ChannelWrapper = ({ children }) => { | ||
return <Channel EmojiPicker={EmojiPicker}>{children}</Channel>; | ||
}; | ||
``` | ||
|
||
### Build your custom `EmojiPicker` (example) | ||
|
||
If `emoji-mart` is too heavy for your use-case and you'd like to build your own you can certainly do so, here's a simple `EmojiPicker` built using `emoji-picker-react` package: | ||
|
||
```tsx | ||
import EmojiPicker from 'emoji-picker-react'; | ||
import { useMessageInputContext } from 'stream-chat-react'; | ||
|
||
export const CustomEmojiPicker = () => { | ||
const [open, setOpen] = useState(false); | ||
|
||
const { insertText } = useMessageInputContext(); | ||
|
||
return ( | ||
<> | ||
<button onClick={() => setOpen((cv) => !cv)}>Open EmojiPicker</button> | ||
|
||
{open && ( | ||
<EmojiPicker | ||
onEmojiClick={(emoji, event) => { | ||
event.preventDefault(); // prevents cursor from changing place in the input | ||
insertText(emoji.emoji); | ||
}} | ||
/> | ||
)} | ||
</> | ||
); | ||
}; | ||
|
||
// and apply it to the Channel (component context) | ||
``` | ||
|
||
You can make the component slightly better using `FloatingUI` by wrapping the actual picker element to make it float perfectly positioned above the button. See the source of the component which comes with the SDK for inspiration. | ||
{/* TODO: provide link to the source (once ready) */} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import React, { useEffect, useState } from 'react'; | ||
import { usePopper } from 'react-popper'; | ||
|
||
// TODO: replace with emoji-mart | ||
import Picker from 'emoji-picker-react'; | ||
|
||
import { | ||
ThemeVersion, | ||
useChatContext, | ||
useMessageInputContext, | ||
useTranslationContext, | ||
} from '../../context'; | ||
import { EmojiIconLarge, EmojiPickerIcon } from './icons'; | ||
import { Tooltip } from '../Tooltip'; | ||
|
||
const classNames: Record< | ||
ThemeVersion, | ||
Record< | ||
'buttonClassName' | 'emojiPickerContainerClassName' | 'wrapperClassName', | ||
string | undefined | ||
> | ||
> = { | ||
1: { | ||
buttonClassName: 'str-chat__input-flat-emojiselect', | ||
emojiPickerContainerClassName: undefined, | ||
wrapperClassName: 'str-chat__emojiselect-wrapper', | ||
}, | ||
2: { | ||
buttonClassName: 'str-chat__emoji-picker-button', | ||
emojiPickerContainerClassName: 'str-chat__message-textarea-emoji-picker-container', | ||
wrapperClassName: 'str-chat__message-textarea-emoji-picker', | ||
}, | ||
}; | ||
|
||
// TODO: handle small variant (MessageInputSmall) | ||
export const EmojiPicker = () => { | ||
const { themeVersion } = useChatContext('EmojiPicker'); | ||
const { t } = useTranslationContext('EmojiPicker'); | ||
const { insertText, textareaRef } = useMessageInputContext('EmojiPicker'); | ||
const [displayPicker, setDisplayPicker] = useState(false); | ||
const [referenceElement, setReferenceElement] = useState<HTMLButtonElement | null>(null); | ||
const [popperElement, setPopperElement] = useState<HTMLDivElement | null>(null); | ||
const { attributes, styles } = usePopper(referenceElement, popperElement, { | ||
placement: themeVersion === '2' ? 'top-end' : 'top-start', | ||
}); | ||
|
||
const { buttonClassName, emojiPickerContainerClassName, wrapperClassName } = classNames[ | ||
themeVersion | ||
]; | ||
|
||
useEffect(() => { | ||
if (!popperElement || !referenceElement) return; | ||
|
||
const handlePointerDown = (e: PointerEvent) => { | ||
const target = e.target as HTMLElement; | ||
|
||
if (popperElement.contains(target) || referenceElement.contains(target)) return; | ||
|
||
setDisplayPicker(false); | ||
}; | ||
|
||
window.addEventListener('pointerdown', handlePointerDown); | ||
return () => window.removeEventListener('pointerdown', handlePointerDown); | ||
}, [referenceElement, popperElement]); | ||
|
||
return ( | ||
<div className={wrapperClassName}> | ||
{displayPicker && ( | ||
<div | ||
className={emojiPickerContainerClassName} | ||
style={styles.popper} | ||
{...attributes.popper} | ||
ref={setPopperElement} | ||
> | ||
<Picker | ||
lazyLoadEmojis | ||
onEmojiClick={(e) => { | ||
insertText(e.emoji); | ||
textareaRef.current?.focus(); | ||
}} | ||
/> | ||
</div> | ||
)} | ||
{themeVersion === '1' && ( | ||
<Tooltip> | ||
{displayPicker ? t<string>('Close emoji picker') : t<string>('Open emoji picker')} | ||
</Tooltip> | ||
)} | ||
<button | ||
aria-label='Emoji picker' | ||
className={buttonClassName} | ||
onClick={() => setDisplayPicker((cv) => !cv)} | ||
ref={setReferenceElement} | ||
type='button' | ||
> | ||
{themeVersion === '2' ? <EmojiPickerIcon /> : <EmojiIconLarge />} | ||
</button> | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters