fix(ui): resolve 29 native UI/UX inconsistencies from folds design audit
Fixes N1–N94 findings from LOTUS_BUGS.md audit pass. Key changes: - ProfileDecoration: raw <button> → folds <Button> for save/remove; remove undefined --accent-cyan var - UserRoomProfile: textarea border uses color.SurfaceVariant.ContainerLine and config tokens instead of undefined --border-interactive var - LotusToastContainer: z-index raised from 9997 → 10001 so toasts appear above Night Light overlay (9998) and modals (9999) - Message.tsx: DeliveryStatus replaces Unicode glyphs with Icon components; MessageQuickReactions returns null instead of <span />; forward menu item gets correct size="100" on after icon - AudioContent: speed chip variant/radii now matches Play chip (Secondary/300) - ReadReceiptAvatars: pill border/radius/padding → folds config tokens; remove dead receipt-pill-btn className - EventReaders: Header size 600→500; close button gets radii="300"; borderBottom shorthand → borderBottomWidth token; remove raw fontSize - General.tsx: selected background/seasonal picker border uses color.Primary.Main instead of color.Critical.Main (error red) - RoomInsights: SectionHeader drops textTransform/letterSpacing/opacity; chart borderRadius → config tokens; remove raw fontSize:9; warning banner → SequenceCard - RoomProfile.tsx: formatting toolbar raw <button> → folds <Button>; topic read-mode renders formatted_body via sanitizeCustomHtml - MsgTypeRenderers: location Open button Chip→Button; opacity:0.65→priority - UploadCardRenderer: caption raw <input> → folds <Input> - VoiceMessageRecorder: replace undefined --bg-surface-variant/--tc-* vars with color.* tokens; replace bare <audio controls> with IconButton play/pause toggle - App.tsx: mention highlight uses WCAG 2.1 relative luminance (gamma linearization) instead of simplified approximation; border now rgba semi-transparent instead of same color as background - RoomNavItem: Mute MenuItem icon moved to before prop - SearchFilters: HasLink chip variant="Success" outlined to match filter bar - RoomViewHeader: Server Notice chip radii Pill→300; fix jotai import order - Fix ESLint import/order errors in DeviceVerificationSetup, RoomTopicViewer, MediaGallery, and RoomViewHeader Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -25,8 +25,8 @@ import {
|
||||
Input,
|
||||
Badge,
|
||||
RectCords,
|
||||
color,
|
||||
} from 'folds';
|
||||
import { color } from 'folds';
|
||||
import { SearchOrderBy } from 'matrix-js-sdk';
|
||||
import FocusTrap from 'focus-trap-react';
|
||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||
@@ -374,7 +374,10 @@ function SelectSenderButton({ selectedSenders, onChange }: SelectSenderButtonPro
|
||||
const searchUser = useDebounce(_searchUser, SEARCH_DEBOUNCE_OPTS);
|
||||
const handleSearchChange: ChangeEventHandler<HTMLInputElement> = (evt) => {
|
||||
const value = evt.currentTarget.value.trim();
|
||||
if (!value) { resetSearch(); return; }
|
||||
if (!value) {
|
||||
resetSearch();
|
||||
return;
|
||||
}
|
||||
searchUser(value);
|
||||
};
|
||||
|
||||
@@ -419,14 +422,30 @@ function SelectSenderButton({ selectedSenders, onChange }: SelectSenderButtonPro
|
||||
>
|
||||
<Menu variant="Surface" style={{ width: toRem(250) }}>
|
||||
<Box direction="Column" style={{ maxHeight: toRem(400), maxWidth: toRem(300) }}>
|
||||
<Box shrink="No" direction="Column" gap="100" style={{ padding: config.space.S200, paddingBottom: 0 }}>
|
||||
<Box
|
||||
shrink="No"
|
||||
direction="Column"
|
||||
gap="100"
|
||||
style={{ padding: config.space.S200, paddingBottom: 0 }}
|
||||
>
|
||||
<Text size="L400">From</Text>
|
||||
<Input onChange={handleSearchChange} size="300" radii="300" placeholder="Search people..." />
|
||||
<Input
|
||||
onChange={handleSearchChange}
|
||||
size="300"
|
||||
radii="300"
|
||||
placeholder="Search people..."
|
||||
/>
|
||||
</Box>
|
||||
<Scroll ref={scrollRef} size="300" hideTrack>
|
||||
<Box direction="Column" gap="100" style={{ padding: config.space.S200, paddingRight: 0 }}>
|
||||
<Box
|
||||
direction="Column"
|
||||
gap="100"
|
||||
style={{ padding: config.space.S200, paddingRight: 0 }}
|
||||
>
|
||||
{users.length === 0 && (
|
||||
<Text style={{ padding: config.space.S400 }} size="T300" align="Center">No match found!</Text>
|
||||
<Text style={{ padding: config.space.S400 }} size="T300" align="Center">
|
||||
No match found!
|
||||
</Text>
|
||||
)}
|
||||
<div style={{ position: 'relative', height: virtualizer.getTotalSize() }}>
|
||||
{vItems.map((vItem) => {
|
||||
@@ -450,7 +469,9 @@ function SelectSenderButton({ selectedSenders, onChange }: SelectSenderButtonPro
|
||||
aria-pressed={selected}
|
||||
before={<Icon size="50" src={Icons.User} />}
|
||||
>
|
||||
<Text truncate size="T300">{name}</Text>
|
||||
<Text truncate size="T300">
|
||||
{name}
|
||||
</Text>
|
||||
</MenuItem>
|
||||
</VirtualTile>
|
||||
);
|
||||
@@ -467,7 +488,14 @@ function SelectSenderButton({ selectedSenders, onChange }: SelectSenderButtonPro
|
||||
<Text size="B300">Save</Text>
|
||||
)}
|
||||
</Button>
|
||||
<Button size="300" radii="300" variant="Secondary" fill="Soft" onClick={handleDeselectAll} disabled={!localSelected || localSelected.length === 0}>
|
||||
<Button
|
||||
size="300"
|
||||
radii="300"
|
||||
variant="Secondary"
|
||||
fill="Soft"
|
||||
onClick={handleDeselectAll}
|
||||
disabled={!localSelected || localSelected.length === 0}
|
||||
>
|
||||
<Text size="B300">Deselect All</Text>
|
||||
</Button>
|
||||
</Box>
|
||||
@@ -477,7 +505,9 @@ function SelectSenderButton({ selectedSenders, onChange }: SelectSenderButtonPro
|
||||
}
|
||||
>
|
||||
<Chip
|
||||
onClick={(e: React.MouseEvent<HTMLButtonElement>) => setMenuAnchor(e.currentTarget.getBoundingClientRect())}
|
||||
onClick={(e: React.MouseEvent<HTMLButtonElement>) =>
|
||||
setMenuAnchor(e.currentTarget.getBoundingClientRect())
|
||||
}
|
||||
variant="SurfaceVariant"
|
||||
radii="Pill"
|
||||
before={<Icon size="100" src={Icons.User} />}
|
||||
@@ -529,22 +559,28 @@ function DateRangeButton({ fromTs, toTs, onChange }: DateRangeButtonProps) {
|
||||
<Box direction="Column" gap="100">
|
||||
<Text size="L400">Quick pick</Text>
|
||||
<Box gap="100" wrap="Wrap">
|
||||
{([
|
||||
{ label: 'Today', days: 0 },
|
||||
{ label: 'Last week', days: 7 },
|
||||
{ label: 'Last month', days: 30 },
|
||||
{ label: 'Last year', days: 365 },
|
||||
] as const).map(({ label: l, days }) => {
|
||||
{(
|
||||
[
|
||||
{ label: 'Today', days: 0 },
|
||||
{ label: 'Last week', days: 7 },
|
||||
{ label: 'Last month', days: 30 },
|
||||
{ label: 'Last year', days: 365 },
|
||||
] as const
|
||||
).map(({ label: l, days }) => {
|
||||
const now = Date.now();
|
||||
const from = days === 0
|
||||
? new Date().setHours(0, 0, 0, 0)
|
||||
: now - days * 24 * 60 * 60 * 1000;
|
||||
const from =
|
||||
days === 0
|
||||
? new Date().setHours(0, 0, 0, 0)
|
||||
: now - days * 24 * 60 * 60 * 1000;
|
||||
return (
|
||||
<Chip
|
||||
key={l}
|
||||
radii="Pill"
|
||||
variant="SurfaceVariant"
|
||||
onClick={() => { onChange(from, now); setMenuAnchor(undefined); }}
|
||||
onClick={() => {
|
||||
onChange(from, now);
|
||||
setMenuAnchor(undefined);
|
||||
}}
|
||||
>
|
||||
<Text size="T200">{l}</Text>
|
||||
</Chip>
|
||||
@@ -746,13 +782,11 @@ export function SearchFilters({
|
||||
</Chip>
|
||||
);
|
||||
})}
|
||||
<SelectSenderButton
|
||||
selectedSenders={selectedSenders}
|
||||
onChange={onSelectedSendersChange}
|
||||
/>
|
||||
<SelectSenderButton selectedSenders={selectedSenders} onChange={onSelectedSendersChange} />
|
||||
<Box grow="Yes" data-spacing-node />
|
||||
<Chip
|
||||
variant={containsUrl ? 'Primary' : 'SurfaceVariant'}
|
||||
variant={containsUrl ? 'Success' : 'SurfaceVariant'}
|
||||
outlined={!!containsUrl}
|
||||
radii="Pill"
|
||||
aria-pressed={!!containsUrl}
|
||||
before={<Icon size="100" src={Icons.Link} />}
|
||||
@@ -761,7 +795,10 @@ export function SearchFilters({
|
||||
<Icon
|
||||
size="50"
|
||||
src={Icons.Cross}
|
||||
onClick={(e) => { e.stopPropagation(); onContainsUrlChange(undefined); }}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onContainsUrlChange(undefined);
|
||||
}}
|
||||
/>
|
||||
) : undefined
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user