Skip to content

Commit a4b1c26

Browse files
authored
fix(Icons, RTL): update icon catalog, RTL layout fixes, dark mode & thread voice recording (#3090)
### 🎯 Goal Sync remaining out-of-date icons with the refreshed Phosphor Line SVG catalog, add automatic RTL mirroring for directional icons, comprehensively fix RTL layout issues across the SDK, fix reaction count dark mode support, and enable voice recording in threads. ### 🛠 Implementation details **Icon catalog sync** (follows up on #3080): - **`IconCrossSmall` → `IconXmarkSmall`**: updated path from `xmark-small.svg` - **`IconExclamationTriangle` → `IconExclamationTriangleFill`**: upgraded from 16×16 to 20×20 filled variant - **`IconEyeOpen` → `IconEyeFill`**: upgraded from 16×16 to 20×20 filled variant - **`IconGiphy`**: synced precision updates - All renames include `// was:` comments; updated all consumer imports **RTL icon mirroring**: - Extended `createIcon`'s `defaultProps` type to accept `data-*` attributes - Flagged 12 directional icons with `{ 'data-rtl-mirror': '' }` via `defaultProps` - Added CSS rule: `[dir='rtl'] .str-chat__icon[data-rtl-mirror] { transform: scaleX(-1) }` - Mirrored icons: `IconArrowLeft`, `IconArrowUpRight`, `IconReply`, `IconChevronLeft`, `IconChevronRight`, `IconSidebar`, `IconSearch`, `IconMute`, `IconSend`, `IconVideo`, `IconVideoFill`, `IconAudio` **RTL layout fixes** (comprehensive audit — physical → logical CSS properties): - **ContextMenu**: `text-align: left` → `start` - **AttachmentPreview**: `right` → `inset-inline-end` (remove button), `padding-right` → `padding-inline-end`, `left` → `inset-inline-start` (video indicator, loading/error icons) - **AudioPlayback**: `left` → `inset-inline-start` on progress indicators (CSS + inline), gradient direction flips in RTL - **AudioRecorder**: `padding-left` → `padding-inline-start`, removed arbitrary `max-width` caps on waveform containers - **Message**: `margin-left`/`margin-right` → logical on edited indicator and reactions host, `text-align: right` → `end` - **Reply connector**: physical `border-*`/`left`/`right` → logical `border-inline-*`/`border-end-*`/`inset-inline-*` on `::after` pseudo-element - **AvatarStack**: `margin-left` → `margin-inline-start` - **Avatar**: `left` → `inset-inline-start` (status badge) - **GroupAvatar**: `left`/`right` → `inset-inline-start`/`end` (grid positions + online badge) - **ChatView**: `border-right` → `border-inline-end`, `left` → `inset-inline-start` (tooltip) - **ChannelListItem**: `right` → `inset-inline-end` (action buttons) - **Gallery**: `left`/`right` → logical (nav buttons) - **Giphy**: `left` → `inset-inline-start` (badge) - **Modal**: `right` → `inset-inline-end` (close button) - **Callout**: `right` → `inset-inline-end` (close button) - **ScrollToLatestMessage**: `right` → `inset-inline-end` (unread badge) - **MessageReactionsDetail**: `left` → `inset-inline-start`, physical border-radius → logical - **QuotedMessagePreview**: `left` → `inset-inline-start` (play indicator) - **TextInput**: `margin-right` → `margin-inline-end` (trailing slot) **Dark mode fix**: - **Reactions**: applied `color: var(--reaction-text)` to reaction button mixin — the variable was defined for light/dark modes but never used, causing invisible count text in dark mode **Example app fixes**: - Panel resize logic: detect `direction` at drag start and flip `clientX` calculations for RTL - Thread composer: enable `audioRecordingEnabled` via `additionalMessageComposerProps` **Tests**: updated 3 assertions (`left` → `inset-inline-start`) and refreshed 4 snapshots. ### 🎨 UI Changes - No visual changes in LTR light mode - In RTL (`dir="rtl"`): icons auto-flip, all layout elements position correctly, waveforms respect reading direction, avatar stacks/group avatars mirror properly, context menus/modals/callouts align to correct side - In dark mode: reaction count text is now visible - Voice recording now available in thread composer (example app)
1 parent 6239895 commit a4b1c26

40 files changed

Lines changed: 137 additions & 108 deletions

File tree

examples/vite/src/AppSettings/ActionsMenu/NotificationPromptDialog.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
IconExclamationMark,
1515
IconClock,
1616
IconXmark,
17-
IconExclamationTriangle,
17+
IconExclamationTriangleFill,
1818
IconPlusSmall,
1919
NumericInput,
2020
Prompt,
@@ -53,7 +53,7 @@ const severityIcons: Partial<
5353
info: IconExclamationMark,
5454
loading: IconRefresh,
5555
success: IconCheckmark,
56-
warning: IconExclamationTriangle,
56+
warning: IconExclamationTriangleFill,
5757
};
5858

5959
const directionIcons: Record<

examples/vite/src/ChatLayout/Panels.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ const ChannelThreadPanel = () => {
3737
'app-chat-thread-panel--open': isOpen,
3838
})}
3939
>
40-
<Thread virtualized />
40+
<Thread
41+
additionalMessageComposerProps={{ audioRecordingEnabled: true }}
42+
virtualized
43+
/>
4144
</WithDragAndDropUpload>
4245
</>
4346
);
@@ -155,7 +158,10 @@ export const ThreadsPanels = ({
155158
<ChatView.ThreadAdapter>
156159
<WithDragAndDropUpload className='str-chat__dropzone-root--thread'>
157160
<WithComponents overrides={{ TypingIndicator }}>
158-
<Thread virtualized />
161+
<Thread
162+
additionalMessageComposerProps={{ audioRecordingEnabled: true }}
163+
virtualized
164+
/>
159165
</WithComponents>
160166
</WithDragAndDropUpload>
161167
</ChatView.ThreadAdapter>

examples/vite/src/ChatLayout/Resize.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -259,11 +259,15 @@ export const SidebarResizeHandle = ({
259259
? 'collapsed'
260260
: 'expanded';
261261

262+
const isRtl = getComputedStyle(appLayoutElement).direction === 'rtl';
263+
262264
beginHorizontalResize({
263265
bodyClassName: 'app-chat-resizing-sidebar',
264266
handle: event.currentTarget,
265267
onMove: (pointerEvent) => {
266-
const nextWidth = pointerEvent.clientX - layoutBounds.left;
268+
const nextWidth = isRtl
269+
? layoutBounds.right - pointerEvent.clientX
270+
: pointerEvent.clientX - layoutBounds.left;
267271
const maxWidth = layoutBounds.width - MESSAGE_VIEW_MIN_WIDTH;
268272
const shouldCollapse = nextWidth < LEFT_PANEL_MIN_WIDTH;
269273

@@ -361,12 +365,15 @@ export const ThreadResizeHandle = ({ isOpen }: { isOpen: boolean }) => {
361365
const dragState = {
362366
width: threadPanelWidthRef.current,
363367
};
368+
const isRtl = getComputedStyle(appLayoutElement).direction === 'rtl';
364369

365370
beginHorizontalResize({
366371
bodyClassName: 'app-chat-resizing-thread',
367372
handle: event.currentTarget,
368373
onMove: (pointerEvent) => {
369-
const nextWidth = containerBounds.right - pointerEvent.clientX;
374+
const nextWidth = isRtl
375+
? pointerEvent.clientX - containerBounds.left
376+
: containerBounds.right - pointerEvent.clientX;
370377
const maxWidth = containerBounds.width - MESSAGE_VIEW_MIN_WIDTH;
371378
const width = clamp(nextWidth, THREAD_PANEL_MIN_WIDTH, maxWidth);
372379

src/components/Attachment/VisibilityDisclaimer.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import React from 'react';
2-
import { IconEyeOpen } from '../Icons';
2+
import { IconEyeFill } from '../Icons';
33
import { useTranslationContext } from '../../context';
44

55
export const VisibilityDisclaimer = () => {
66
const { t } = useTranslationContext();
77
return (
88
<div className='str-chat__visibility-disclaimer'>
9-
<IconEyeOpen />
9+
<IconEyeFill />
1010
{t('Only visible to you')}
1111
</div>
1212
);

src/components/Attachment/__tests__/WaveProgressBar.test.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@ describe('WaveProgressBar', () => {
111111
render(<WaveProgressBar seek={vi.fn()} waveformData={originalSample} />);
112112
expect(screen.getAllByTestId(AMPLITUDE_BAR_TEST_ID)).toHaveLength(40);
113113
expect(screen.getByTestId(PROGRESS_INDICATOR_TEST_ID)).toBeInTheDocument();
114-
expect(screen.getByTestId(PROGRESS_INDICATOR_TEST_ID)).toHaveStyle('left: 0px');
114+
expect(screen.getByTestId(PROGRESS_INDICATOR_TEST_ID)).toHaveStyle(
115+
'inset-inline-start: 0px',
116+
);
115117
});
116118

117119
it('is rendered with highlighted bars with non-zero progress', () => {

src/components/Attachment/styling/Giphy.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
align-items: center;
3737
gap: var(--spacing-xxs, 4px);
3838
position: absolute;
39-
left: 8px;
39+
inset-inline-start: 8px;
4040
bottom: 8px;
4141

4242
border-radius: var(--radius-lg, 12px);

src/components/AudioPlayback/components/ProgressBar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export const ProgressBar = ({ className, progress, seek }: ProgressBarProps) =>
4444
<div
4545
className='str-chat__message-attachment-audio-widget--progress-indicator'
4646
ref={setProgressIndicator}
47-
style={{ left: `${indicatorLeft}px` }}
47+
style={{ insetInlineStart: `${indicatorLeft}px` }}
4848
/>
4949
</div>
5050
);

src/components/AudioPlayback/components/WaveProgressBar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ export const WaveProgressBar = ({
156156
data-testid='wave-progress-bar-progress-indicator'
157157
ref={setProgressIndicator}
158158
style={{
159-
left: `${indicatorLeft}px`,
159+
insetInlineStart: `${indicatorLeft}px`,
160160
}}
161161
/>
162162
</div>

src/components/AudioPlayback/styling/ProgressBar.scss

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@
66
min-width: 0;
77
cursor: pointer;
88
background: linear-gradient(
9-
to right,
9+
to var(--str-chat__progress-direction, right),
1010
var(--str-chat__primary-color)
1111
var(--str-chat__message-attachment-audio-widget-progress),
1212
var(--str-chat__disabled-color)
1313
var(--str-chat__message-attachment-audio-widget-progress)
1414
);
15+
16+
[dir='rtl'] & {
17+
--str-chat__progress-direction: left;
18+
}
1519
border-radius: calc(var(--str-chat__spacing-px) * 5);
1620

1721
.str-chat__message-attachment-audio-widget--progress-indicator {

src/components/AudioPlayback/styling/WaveProgressBar.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333

3434
.str-chat__wave-progress-bar__progress-indicator {
3535
position: absolute;
36-
left: 0;
36+
inset-inline-start: 0;
3737
// todo: CSS use semantic variable instead of --base-white
3838
border: 2px solid var(--base-white);
3939
box-shadow: var(--light-elevation-3);

0 commit comments

Comments
 (0)