fix(direct): lock DmStreamRow title-block height so the room name stops jumping when row 2 collapses on hover

This commit is contained in:
v.lagerev 2026-05-06 16:19:34 +03:00
parent d1bf95f541
commit 34fe869e95
2 changed files with 67 additions and 42 deletions

View file

@ -15,7 +15,6 @@ import {
RectCords,
Spinner,
Text,
color,
config,
toRem,
} from 'folds';
@ -62,9 +61,7 @@ import { useCallPreferencesAtom } from '../../state/hooks/callPreferences';
import { useAutoDiscoveryInfo } from '../../hooks/useAutoDiscoveryInfo';
import { livekitSupport } from '../../hooks/useLivekitSupport';
import { timeHourMinute } from '../../utils/time';
const MONO_FONT = '"JetBrains Mono Variable", ui-monospace, monospace';
const ROW_MIN_HEIGHT = toRem(68);
import * as css from './styles.css';
type DmStreamRowMenuProps = {
room: Room;
@ -346,7 +343,7 @@ export function DmStreamRow({ room, selected, notificationMode, linkPath }: DmSt
aria-selected={selected}
data-hover={!!menuAnchor}
onContextMenu={handleContextMenu}
style={{ minHeight: ROW_MIN_HEIGHT, boxSizing: 'border-box' }}
className={css.DmRowOuter}
{...hoverProps}
{...focusWithinProps}
>
@ -357,11 +354,7 @@ export function DmStreamRow({ room, selected, notificationMode, linkPath }: DmSt
grow="Yes"
alignItems="Center"
gap="300"
style={{
minHeight: ROW_MIN_HEIGHT,
boxSizing: 'border-box',
padding: `${toRem(8)} 0`,
}}
className={css.DmRowInner}
>
<Avatar size="300" radii="400">
<RoomAvatar
@ -391,56 +384,34 @@ export function DmStreamRow({ room, selected, notificationMode, linkPath }: DmSt
direction="Column"
grow="Yes"
gap="100"
style={{ minWidth: 0, overflow: 'hidden' }}
className={css.DmRowText}
>
<Box as="span" alignItems="Baseline" gap="200" style={{ minWidth: 0 }}>
<Box
as="span"
grow="Yes"
alignItems="Baseline"
style={{ minWidth: 0, overflow: 'hidden' }}
>
<Box as="span" alignItems="Baseline" gap="200">
<Box as="span" grow="Yes" className={css.DmRowTextStretch}>
<Text
as="span"
priority={unread ? '500' : '400'}
size="T300"
truncate
style={{ fontWeight: 600 }}
className={css.DmRowName}
>
{roomName}
</Text>
</Box>
{timeLabel && !optionsVisible && (
<span
style={{
fontFamily: MONO_FONT,
fontSize: toRem(10.5),
color: color.Surface.OnContainer,
opacity: 0.55,
fontVariantNumeric: 'tabular-nums',
flexShrink: 0,
whiteSpace: 'nowrap',
}}
>
{timeLabel}
</span>
<span className={css.DmRowTime}>{timeLabel}</span>
)}
</Box>
<Box as="span" alignItems="Center" gap="200" style={{ minWidth: 0 }}>
<Box as="span" grow="Yes" style={{ minWidth: 0, overflow: 'hidden' }}>
<Box as="span" alignItems="Center" gap="200">
<Box as="span" grow="Yes" className={css.DmRowTextStretch}>
{typingMember.length > 0 ? (
<Box as="span" alignItems="Center" gap="100">
<TypingIndicator size="300" disableAnimation />
</Box>
<TypingIndicator size="300" disableAnimation />
) : (
<Text
as="span"
size="T200"
truncate
style={{
opacity: unread ? 0.85 : 0.6,
fontWeight: unread ? 500 : 400,
}}
className={unread ? css.DmRowPreviewUnread : css.DmRowPreview}
>
{previewText}
</Text>

View file

@ -1,5 +1,5 @@
import { style } from '@vanilla-extract/css';
import { config } from 'folds';
import { color, config, toRem } from 'folds';
export const CategoryButton = style({
flexGrow: 1,
@ -7,3 +7,57 @@ export const CategoryButton = style({
export const CategoryButtonIcon = style({
opacity: config.opacity.P400,
});
const DmRowMinHeight = toRem(68);
const DmRowTitleBlockHeight = toRem(40);
export const DmRowOuter = style({
minHeight: DmRowMinHeight,
boxSizing: 'border-box',
});
export const DmRowInner = style({
minHeight: DmRowMinHeight,
boxSizing: 'border-box',
padding: `${toRem(8)} 0`,
});
// Locks the title block to its natural 2-line height so the name does
// not jump vertically when the second row's content (preview / unread
// badge / notification icon) collapses to 0px on hover. Bridged rooms
// with no m.room.message yet but a synced unread badge are the obvious
// trigger; the same pathology applies to any (empty preview + has unread)
// pair, so the fix is room-class-agnostic.
export const DmRowText = style({
minWidth: 0,
overflow: 'hidden',
minHeight: DmRowTitleBlockHeight,
});
export const DmRowTextStretch = style({
minWidth: 0,
overflow: 'hidden',
});
export const DmRowName = style({
fontWeight: 600,
});
export const DmRowTime = style({
fontFamily: '"JetBrains Mono Variable", ui-monospace, monospace',
fontSize: toRem(10.5),
color: color.Surface.OnContainer,
opacity: 0.55,
fontVariantNumeric: 'tabular-nums',
flexShrink: 0,
whiteSpace: 'nowrap',
});
export const DmRowPreview = style({
opacity: 0.6,
});
export const DmRowPreviewUnread = style({
opacity: 0.85,
fontWeight: 500,
});