style(settings): replace the boxed setting cards with a flat edge-to-edge list parted by hairlines
This commit is contained in:
parent
390149d1f6
commit
587d117f96
5 changed files with 73 additions and 51 deletions
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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({
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue