Skip to content

Commit

Permalink
fix: prevent layout shifts in reactions modal (#2272)
Browse files Browse the repository at this point in the history
πŸš‚ GetStream/stream-chat-css#270

### To-Do

- [x] Release and bump updated CSS
  • Loading branch information
myandrienko committed Feb 21, 2024
1 parent 7f350a0 commit 706cf3d
Show file tree
Hide file tree
Showing 7 changed files with 46 additions and 26 deletions.
10 changes: 9 additions & 1 deletion docusaurus/docs/React/release-guides/upgrade-to-v11.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import GHComponentLink from '../_docusaurus-components/GHComponentLink';

## Introducing new reactions

When it came to developer experience regarding customization of the reaction components our team noticed that our integrators generally strugled to make more advanced adjustments to reactions without having to rebuild the whole [component set](../components/message-components/reactions.mdx). The whole process has been quite unintuitive and that's why this update aims at making adjusting your reactions much easier.
When it came to developer experience regarding customization of the reaction components our team noticed that our integrators generally struggled to make more advanced adjustments to reactions without having to rebuild the whole [component set](../components/message-components/reactions.mdx). The whole process has been quite unintuitive and that's why this update aims at making adjusting your reactions much easier.

### Main reasons for a revamp

Expand Down Expand Up @@ -52,6 +52,14 @@ Beware that sixth reaction type `angry` has been removed in this update, if you

### Custom reaction types and components

The default <GHComponentLink text='StreamEmoji' path='/Reactions/StreamEmoji.tsx'/> component is rendered with the fixed size. You can change the size of the rendered emoji using `--str-chat__stream-emoji-size` CSS variable:

```tsx
<div style={{ '--str-chat__stream-emoji-size': '2em' }}>
<StreamEmoji fallback='πŸ˜‚' type='haha' />
</div>
```

It's possible to supply your own reaction types and components to represent such reactions - let's implement reaction of type `rick_roll` to render a Rick Roll GIF and define override for default type `love`:

```tsx
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@
"@semantic-release/changelog": "^6.0.2",
"@semantic-release/git": "^10.0.1",
"@stream-io/rollup-plugin-node-builtins": "^2.1.5",
"@stream-io/stream-chat-css": "^4.7.0",
"@stream-io/stream-chat-css": "^4.7.1",
"@testing-library/jest-dom": "^6.1.4",
"@testing-library/react": "^13.1.1",
"@testing-library/react-hooks": "^8.0.0",
Expand Down
4 changes: 2 additions & 2 deletions src/components/Reactions/ReactionsListModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export function ReactionsListModal({
key={reactionType}
onClick={() => onSelectedReactionTypeChange?.(reactionType)}
>
<span className='emoji str-chat__message-reaction-emoji'>
<span className='emoji str-chat__message-reaction-emoji str-chat__message-reaction-emoji--with-fallback'>
<EmojiComponent />
</span>
&nbsp;
Expand All @@ -69,7 +69,7 @@ export function ReactionsListModal({
)}
</div>
{SelectedEmojiComponent && (
<div className='emoji str-chat__message-reaction-emoji str-chat__message-reaction-emoji-big'>
<div className='emoji str-chat__message-reaction-emoji str-chat__message-reaction-emoji--with-fallback str-chat__message-reaction-emoji-big'>
<SelectedEmojiComponent />
</div>
)}
Expand Down
40 changes: 24 additions & 16 deletions src/components/Reactions/SpriteImage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export type SpriteImageProps = {
spriteUrl: string;
fallback?: React.ReactNode;
height?: number;
style?: React.CSSProperties;
width?: number;
};

Expand All @@ -19,6 +20,7 @@ export const SpriteImage = ({
position,
rows,
spriteUrl,
style,
width,
}: SpriteImageProps) => {
const [[spriteWidth, spriteHeight], setSpriteDimensions] = useState([0, 0]);
Expand All @@ -28,28 +30,34 @@ export const SpriteImage = ({
}, [spriteUrl]);

const [x, y] = position;
const spriteItemWidth = spriteWidth / columns;
const spriteItemHeight = spriteHeight / rows;

let resizeRatio = 1;

if (!width && height) resizeRatio = height / spriteItemHeight;
if (width && !height) resizeRatio = width / spriteItemWidth;

if (resizeRatio === Infinity) resizeRatio = 1;

if (!spriteHeight || !spriteWidth) return <>{fallback}</>;

return (
<div
data-testid='sprite-image'
style={{
backgroundImage: `url('${spriteUrl}')`,
backgroundPosition: `${x * (100 / (columns - 1))}% ${y * (100 / (rows - 1))}%`,
backgroundSize: `${columns * 100}% ${rows * 100}%`,
height: height ?? spriteItemHeight * resizeRatio,
width: width ?? spriteItemWidth * resizeRatio,
}}
style={
{
...style,
'--str-chat__sprite-image-resize-ratio':
'var(--str-chat__sprite-image-resize-ratio-x, var(--str-chat__sprite-image-resize-ratio-y, 1))',
'--str-chat__sprite-image-resize-ratio-x':
'calc(var(--str-chat__sprite-image-width) / var(--str-chat__sprite-item-width))',
'--str-chat__sprite-image-resize-ratio-y':
'calc(var(--str-chat__sprite-image-height) / var(--str-chat__sprite-item-height))',
'--str-chat__sprite-item-height': `${spriteHeight / rows}`,
'--str-chat__sprite-item-width': `${spriteWidth / columns}`,
...(Number.isFinite(height) ? { '--str-chat__sprite-image-height': `${height}px` } : {}),
...(Number.isFinite(width) ? { '--str-chat__sprite-image-width': `${width}px` } : {}),
backgroundImage: `url('${spriteUrl}')`,
backgroundPosition: `${x * (100 / (columns - 1))}% ${y * (100 / (rows - 1))}%`,
backgroundSize: `${columns * 100}% ${rows * 100}%`,
height:
'var(--str-chat__sprite-image-height, calc(var(--str-chat__sprite-item-height) * var(--str-chat__sprite-image-resize-ratio)))',
width:
'var(--str-chat__sprite-image-width, calc(var(--str-chat__sprite-item-width) * var(--str-chat__sprite-image-resize-ratio)))',
} as React.CSSProperties
}
/>
);
};
6 changes: 5 additions & 1 deletion src/components/Reactions/StreamEmoji.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,14 @@ export const StreamEmoji = ({
<SpriteImage
columns={2}
fallback={fallback}
height={18}
position={position}
rows={3}
spriteUrl={STREAM_SPRITE_URL}
style={
{
'--str-chat__sprite-image-height': 'var(--str-chat__stream-emoji-size, 18px)',
} as React.CSSProperties
}
/>
);
};
2 changes: 1 addition & 1 deletion src/components/Reactions/hooks/useFetchReactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ export function useFetchReactions<
const {
handleFetchReactions: contextHandleFetchReactions,
} = useMessageContext<StreamChatGenerics>('useFetchReactions');
const [isLoading, setIsLoading] = useState(false);
const [reactions, setReactions] = useState<ReactionResponse[]>([]);
const { handleFetchReactions: propHandleFetchReactions, shouldFetch } = options;
const [isLoading, setIsLoading] = useState(shouldFetch);
const handleFetchReactions = propHandleFetchReactions ?? contextHandleFetchReactions;

useEffect(() => {
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2186,10 +2186,10 @@
crypto-browserify "^3.11.0"
process-es6 "^0.11.2"

"@stream-io/stream-chat-css@^4.7.0":
version "4.7.0"
resolved "https://registry.yarnpkg.com/@stream-io/stream-chat-css/-/stream-chat-css-4.7.0.tgz#9fb46d19268b9401a1f3040e11a6a6e1c2b85f7a"
integrity sha512-23QmqoZJ3jdz4SQ+HQvHj2D7It3PgQ9xv1rbnjjWnnYVUWxojFhvqw0+f7j+Gh2hjHpJrBZel4lZDQHiK3uQ9g==
"@stream-io/stream-chat-css@^4.7.1":
version "4.7.1"
resolved "https://registry.yarnpkg.com/@stream-io/stream-chat-css/-/stream-chat-css-4.7.1.tgz#82e058a3cf9031852875266f4c7cc1029e8cb193"
integrity sha512-Vshgw6VyZU/TtxfXb/gAxWhfgq3Pc0yTPutqHXWCI1qvFfma+ZgyA0AlAu3zqwHP60ubeUJZ0W01/qL/9u7Avg==

"@stream-io/transliterate@^1.5.5":
version "1.5.5"
Expand Down

0 comments on commit 706cf3d

Please sign in to comment.