style(settings): replace the boxed setting cards with a flat edge-to-edge list parted by hairlines

This commit is contained in:
heaven 2026-06-04 02:40:50 +03:00
parent 390149d1f6
commit 587d117f96
5 changed files with 73 additions and 51 deletions

View file

@ -17,8 +17,7 @@ import Linkify from 'linkify-react';
import classNames from 'classnames'; import classNames from 'classnames';
import { JoinRule, MatrixError } from 'matrix-js-sdk'; import { JoinRule, MatrixError } from 'matrix-js-sdk';
import type { StateEvents } from 'matrix-js-sdk'; import type { StateEvents } from 'matrix-js-sdk';
import { SequenceCard } from '../../../components/sequence-card'; import { SectionLabel, SettingRule } from '../../settings/styles.css';
import { SequenceCardStyle } from '../../room-settings/styles.css';
import { useRoom } from '../../../hooks/useRoom'; import { useRoom } from '../../../hooks/useRoom';
import { import {
useRoomAvatar, useRoomAvatar,
@ -298,14 +297,11 @@ export function RoomProfile({ permissions }: RoomProfileProps) {
const handleCloseEdit = useCallback(() => setEdit(false), []); const handleCloseEdit = useCallback(() => setEdit(false), []);
return ( return (
<Box direction="Column" gap="100"> <Box direction="Column" gap="200">
<Text size="L400">{t('RoomSettings.profile')}</Text> <Text as="span" className={SectionLabel}>
<SequenceCard {t('RoomSettings.profile')}
className={SequenceCardStyle} </Text>
variant="SurfaceVariant" <Box direction="Column" gap="400">
direction="Column"
gap="400"
>
{edit ? ( {edit ? (
<RoomProfileEdit <RoomProfileEdit
canEditAvatar={canEditAvatar} canEditAvatar={canEditAvatar}
@ -363,7 +359,8 @@ export function RoomProfile({ permissions }: RoomProfileProps) {
</Box> </Box>
</Box> </Box>
)} )}
</SequenceCard> </Box>
<hr className={SettingRule} />
</Box> </Box>
); );
} }

View file

@ -3,6 +3,7 @@ import { Box, Icon, IconButton, Icons, Scroll, Text } from 'folds';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Page, PageContent, PageHeader } from '../../../components/page'; import { Page, PageContent, PageHeader } from '../../../components/page';
import { SettingsSection } from '../../settings/SettingsSection'; import { SettingsSection } from '../../settings/SettingsSection';
import { SectionLabel } from '../../settings/styles.css';
import { usePowerLevels } from '../../../hooks/usePowerLevels'; import { usePowerLevels } from '../../../hooks/usePowerLevels';
import { useRoom } from '../../../hooks/useRoom'; import { useRoom } from '../../../hooks/useRoom';
import { import {
@ -55,13 +56,17 @@ export function General({ requestClose }: GeneralProps) {
<RoomEncryption permissions={permissions} /> <RoomEncryption permissions={permissions} />
<RoomPublish permissions={permissions} /> <RoomPublish permissions={permissions} />
</SettingsSection> </SettingsSection>
<Box direction="Column" gap="100"> <Box direction="Column" gap="200">
<Text size="L400">{t('RoomSettings.addresses')}</Text> <Text as="span" className={SectionLabel}>
{t('RoomSettings.addresses')}
</Text>
<RoomPublishedAddresses permissions={permissions} /> <RoomPublishedAddresses permissions={permissions} />
<RoomLocalAddresses permissions={permissions} /> <RoomLocalAddresses permissions={permissions} />
</Box> </Box>
<Box direction="Column" gap="100"> <Box direction="Column" gap="200">
<Text size="L400">{t('RoomSettings.advanced_options')}</Text> <Text as="span" className={SectionLabel}>
{t('RoomSettings.advanced_options')}
</Text>
<RoomUpgrade permissions={permissions} requestClose={requestClose} /> <RoomUpgrade permissions={permissions} requestClose={requestClose} />
</Box> </Box>
</Box> </Box>

View file

@ -1,39 +1,29 @@
import React, { Children, ReactNode } from 'react'; import React, { Children, ReactNode } from 'react';
import { Box, Text } from 'folds'; import { Box, Text } from 'folds';
import { SequenceCard } from '../../components/sequence-card'; import { SectionFootnote, SectionLabel, SettingFlatGroup, SettingFlatRow } from './styles.css';
import { ContainerColorVariants } from '../../styles/ContainerColor.css';
import { SectionFootnote, SectionLabel, SettingRow } from './styles.css';
type SettingsSectionProps = { type SettingsSectionProps = {
// Uppercase muted heading above the panel. Omit for an unlabelled panel. // Uppercase muted heading above the list. Omit for an unlabelled section.
label?: ReactNode; label?: ReactNode;
// Small caption under the label (rare — e.g. a privacy note). // Small caption under the label (rare — e.g. a privacy note).
footnote?: ReactNode; footnote?: ReactNode;
// Surface tone for the rows. Default `Background` = the inset darker panel // Each direct child becomes one row of the flat list. Falsy children
// (#0d0e11) on the SurfaceVariant page (#181a20). // (conditional rows) are dropped so the hairline dividers stay correct.
variant?: NonNullable<ContainerColorVariants>['variant'];
// Each direct child becomes one row of the grouped panel. Falsy children
// (conditional rows) are dropped so dividers/rounding stay correct.
children: ReactNode; children: ReactNode;
}; };
/** /**
* A Dawn grouped settings panel: an uppercase muted label over a single * A Dawn flat settings list: an uppercase muted label over edge-to-edge rows
* outlined card whose rows are separated by hairline dividers (via * parted by hairline dividers no card box or surface fill (iOS/Telegram
* `SequenceCard`'s `mergeBorder` + first/last auto-rounding). Replaces the * grouped-list feel). Replaces the stock-Cinny "floating outlined card per
* stock-Cinny "one floating card per setting" pattern. * setting" pattern.
*/ */
export function SettingsSection({ export function SettingsSection({ label, footnote, children }: SettingsSectionProps) {
label,
footnote,
variant = 'Background',
children,
}: SettingsSectionProps) {
const rows = Children.toArray(children).filter(Boolean); const rows = Children.toArray(children).filter(Boolean);
if (rows.length === 0) return null; if (rows.length === 0) return null;
return ( return (
<Box direction="Column" gap="200"> <Box direction="Column" gap="100">
{label && ( {label && (
<Box direction="Column"> <Box direction="Column">
<Text as="span" className={SectionLabel}> <Text as="span" className={SectionLabel}>
@ -46,23 +36,19 @@ export function SettingsSection({
)} )}
</Box> </Box>
)} )}
<Box direction="Column"> <div className={SettingFlatGroup}>
{rows.map((row, index) => ( {rows.map((row, index) => (
<SequenceCard <div
// Rows are positional and have no stable id; index key is correct // Rows are positional and have no stable id; index key is correct
// here — the list never reorders, only conditionally includes rows. // here — the list never reorders, only conditionally includes rows.
// eslint-disable-next-line react/no-array-index-key // eslint-disable-next-line react/no-array-index-key
key={index} key={index}
variant={variant} className={SettingFlatRow}
outlined
mergeBorder
direction="Column"
className={SettingRow}
> >
{row} {row}
</SequenceCard> </div>
))} ))}
</Box> </div>
</Box> </Box>
); );
} }

View file

@ -5,13 +5,13 @@ import { color, config, toRem } from 'folds';
// Dawn canon's "КОМАНДЫ" / "УЧАСТНИКИ · 28" panel headers (sans, not mono). // Dawn canon's "КОМАНДЫ" / "УЧАСТНИКИ · 28" panel headers (sans, not mono).
export const SectionLabel = style({ export const SectionLabel = style({
display: 'block', display: 'block',
fontSize: toRem(11), fontSize: toRem(12),
lineHeight: toRem(16), lineHeight: toRem(16),
fontWeight: config.fontWeight.W600, fontWeight: config.fontWeight.W600,
letterSpacing: '0.06em', letterSpacing: '0.08em',
textTransform: 'uppercase', textTransform: 'uppercase',
color: color.Surface.OnContainer, color: color.Surface.OnContainer,
opacity: 0.5, opacity: 0.55,
paddingLeft: config.space.S100, paddingLeft: config.space.S100,
paddingRight: config.space.S100, paddingRight: config.space.S100,
}); });
@ -37,6 +37,35 @@ export const SettingRow = style({
paddingRight: config.space.S400, paddingRight: config.space.S400,
}); });
// Dawn flat-list settings — no card box/fill. Rows sit edge-to-edge directly on
// the page, parted by hairlines (iOS/Telegram grouped-list feel). Replaces the
// outlined-filled SequenceCard panel.
export const SettingFlatGroup = style({
display: 'flex',
flexDirection: 'column',
});
export const SettingFlatRow = style({
paddingTop: config.space.S300,
paddingBottom: config.space.S300,
paddingLeft: config.space.S100,
paddingRight: config.space.S100,
selectors: {
'&:not(:last-child)': {
borderBottom: '1px solid rgba(255, 255, 255, 0.08)',
},
},
});
// A full-bleed hairline rule under a section/profile header — the "ДОСТУП ───"
// divider in the flat layout.
export const SettingRule = style({
height: '1px',
border: 'none',
margin: 0,
backgroundColor: 'rgba(255, 255, 255, 0.08)',
});
// JetBrains Mono for technical values — mxid, device ids, version, tokens — // JetBrains Mono for technical values — mxid, device ids, version, tokens —
// the same stack the DM stream / bot surfaces use for handles & timestamps. // the same stack the DM stream / bot surfaces use for handles & timestamps.
export const Mono = style({ export const Mono = style({

View file

@ -3,6 +3,7 @@ import { Box, Icon, IconButton, Icons, Scroll, Text } from 'folds';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Page, PageContent, PageHeader } from '../../../components/page'; import { Page, PageContent, PageHeader } from '../../../components/page';
import { SettingsSection } from '../../settings/SettingsSection'; import { SettingsSection } from '../../settings/SettingsSection';
import { SectionLabel } from '../../settings/styles.css';
import { usePowerLevels } from '../../../hooks/usePowerLevels'; import { usePowerLevels } from '../../../hooks/usePowerLevels';
import { useRoom } from '../../../hooks/useRoom'; import { useRoom } from '../../../hooks/useRoom';
import { import {
@ -51,13 +52,17 @@ export function General({ requestClose }: GeneralProps) {
<RoomJoinRules permissions={permissions} /> <RoomJoinRules permissions={permissions} />
<RoomPublish permissions={permissions} /> <RoomPublish permissions={permissions} />
</SettingsSection> </SettingsSection>
<Box direction="Column" gap="100"> <Box direction="Column" gap="200">
<Text size="L400">{t('RoomSettings.addresses')}</Text> <Text as="span" className={SectionLabel}>
{t('RoomSettings.addresses')}
</Text>
<RoomPublishedAddresses permissions={permissions} /> <RoomPublishedAddresses permissions={permissions} />
<RoomLocalAddresses permissions={permissions} /> <RoomLocalAddresses permissions={permissions} />
</Box> </Box>
<Box direction="Column" gap="100"> <Box direction="Column" gap="200">
<Text size="L400">{t('RoomSettings.advanced_options')}</Text> <Text as="span" className={SectionLabel}>
{t('RoomSettings.advanced_options')}
</Text>
<RoomUpgrade permissions={permissions} requestClose={requestClose} /> <RoomUpgrade permissions={permissions} requestClose={requestClose} />
</Box> </Box>
</Box> </Box>