diff --git a/public/locales/en.json b/public/locales/en.json index 108e7c96..11cb0e08 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -918,7 +918,9 @@ "perm_space_name": "Space Name", "perm_space_topic": "Space Topic", "perm_change_space_access": "Change Space Access", - "perm_upgrade_space": "Upgrade Space" + "perm_upgrade_space": "Upgrade Space", + "settings": "Settings", + "sections": "Sections" }, "Push": { "new_message": "New message", diff --git a/public/locales/ru.json b/public/locales/ru.json index c4ef2b84..6db10048 100644 --- a/public/locales/ru.json +++ b/public/locales/ru.json @@ -936,7 +936,9 @@ "perm_space_name": "Название пространства", "perm_space_topic": "Тема пространства", "perm_change_space_access": "Изменение доступа к пространству", - "perm_upgrade_space": "Обновить пространство" + "perm_upgrade_space": "Обновить пространство", + "settings": "Настройки", + "sections": "Разделы" }, "Push": { "new_message": "Новое сообщение", diff --git a/src/app/features/common-settings/SettingsNav.css.ts b/src/app/features/common-settings/SettingsNav.css.ts new file mode 100644 index 00000000..0bf939ce --- /dev/null +++ b/src/app/features/common-settings/SettingsNav.css.ts @@ -0,0 +1,86 @@ +import { style } from '@vanilla-extract/css'; +import { color, config, DefaultReset, FocusOutline, toRem } from 'folds'; + +// Dawn settings rail — uppercase tracked muted labels, raised active row with a +// violet accent tick, and an eyebrow over the entity name. Shared by room and +// space settings. + +const MUTED = 'rgba(230, 230, 233, 0.45)'; + +export const NavEyebrow = style([ + DefaultReset, + { + display: 'block', + textTransform: 'uppercase', + letterSpacing: '0.1em', + fontSize: toRem(10), + lineHeight: toRem(14), + fontWeight: config.fontWeight.W600, + color: MUTED, + }, +]); + +export const NavSection = style([ + DefaultReset, + { + display: 'block', + textTransform: 'uppercase', + letterSpacing: '0.08em', + fontSize: toRem(11), + lineHeight: toRem(16), + fontWeight: config.fontWeight.W600, + color: MUTED, + padding: `${config.space.S400} ${config.space.S300} ${config.space.S200}`, + }, +]); + +export const NavItem = style([ + DefaultReset, + FocusOutline, + { + position: 'relative', + display: 'flex', + alignItems: 'center', + gap: config.space.S300, + width: '100%', + padding: `${config.space.S300} ${config.space.S300}`, + marginBottom: toRem(2), + borderRadius: config.radii.R400, + color: color.Surface.OnContainer, + cursor: 'pointer', + selectors: { + '&:hover': { backgroundColor: color.Background.ContainerHover }, + '&[aria-pressed=true]': { backgroundColor: color.Background.ContainerActive }, + '&[aria-pressed=true]::before': { + content: '""', + position: 'absolute', + left: toRem(5), + top: '50%', + transform: 'translateY(-50%)', + width: toRem(3), + height: '52%', + borderRadius: toRem(3), + backgroundColor: color.Primary.Main, + }, + }, + }, +]); + +export const NavItemIcon = style({ + opacity: 0.6, + selectors: { + [`${NavItem}[aria-pressed=true] &`]: { + color: color.Primary.Main, + opacity: 1, + }, + }, +}); + +export const NavItemLabel = style({ + fontWeight: config.fontWeight.W500, + selectors: { + [`${NavItem}[aria-pressed=true] &`]: { + fontWeight: config.fontWeight.W600, + }, + }, +}); diff --git a/src/app/features/common-settings/SettingsNav.tsx b/src/app/features/common-settings/SettingsNav.tsx new file mode 100644 index 00000000..998ad95f --- /dev/null +++ b/src/app/features/common-settings/SettingsNav.tsx @@ -0,0 +1,28 @@ +import React, { ReactNode } from 'react'; +import { Icon, IconSrc, Text } from 'folds'; +import * as css from './SettingsNav.css'; + +type SettingsNavItemProps = { + icon: IconSrc; + label: string; + active: boolean; + onClick: () => void; +}; +export function SettingsNavItem({ icon, label, active, onClick }: SettingsNavItemProps) { + return ( + + ); +} + +export function SettingsNavEyebrow({ children }: { children: ReactNode }) { + return {children}; +} + +export function SettingsNavSection({ children }: { children: ReactNode }) { + return {children}; +} diff --git a/src/app/features/room-settings/RoomSettings.tsx b/src/app/features/room-settings/RoomSettings.tsx index 497081b6..89a2e489 100644 --- a/src/app/features/room-settings/RoomSettings.tsx +++ b/src/app/features/room-settings/RoomSettings.tsx @@ -1,8 +1,13 @@ import React, { useMemo, useState } from 'react'; -import { Avatar, Box, config, Icon, IconButton, Icons, IconSrc, MenuItem, Text } from 'folds'; +import { Avatar, Box, Icon, IconButton, Icons, IconSrc, Text } from 'folds'; import { useTranslation } from 'react-i18next'; import { JoinRule } from 'matrix-js-sdk'; import { PageNav, PageNavContent, PageNavHeader, PageRoot } from '../../components/page'; +import { + SettingsNavEyebrow, + SettingsNavItem, + SettingsNavSection, +} from '../common-settings/SettingsNav'; import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize'; import { useMatrixClient } from '../../hooks/useMatrixClient'; import { mxcUrlToHttp } from '../../utils/matrix'; @@ -63,6 +68,7 @@ type RoomSettingsProps = { requestClose: () => void; }; export function RoomSettings({ initialPage, requestClose }: RoomSettingsProps) { + const { t } = useTranslation(); const room = useRoom(); const mx = useMatrixClient(); const useAuthentication = useMediaAuthentication(); @@ -99,8 +105,8 @@ export function RoomSettings({ initialPage, requestClose }: RoomSettingsProps) { screenSize === ScreenSize.Mobile && activePage !== undefined ? undefined : ( - - + + - - {roomName} - + + {t('RoomSettings.settings')} + + {roomName} + + {screenSize === ScreenSize.Mobile && ( @@ -130,25 +139,15 @@ export function RoomSettings({ initialPage, requestClose }: RoomSettingsProps) {
+ {t('RoomSettings.sections')} {menuItems.map((item) => ( - } + icon={item.icon} + label={item.name} + active={activePage === item.page} onClick={() => setActivePage(item.page)} - > - - {item.name} - - + /> ))}
diff --git a/src/app/features/space-settings/SpaceSettings.tsx b/src/app/features/space-settings/SpaceSettings.tsx index 0a4f6d54..2b942e59 100644 --- a/src/app/features/space-settings/SpaceSettings.tsx +++ b/src/app/features/space-settings/SpaceSettings.tsx @@ -1,8 +1,13 @@ import React, { useMemo, useState } from 'react'; -import { Avatar, Box, config, Icon, IconButton, Icons, IconSrc, MenuItem, Text } from 'folds'; +import { Avatar, Box, Icon, IconButton, Icons, IconSrc, Text } from 'folds'; import { JoinRule } from 'matrix-js-sdk'; import { useTranslation } from 'react-i18next'; import { PageNav, PageNavContent, PageNavHeader, PageRoot } from '../../components/page'; +import { + SettingsNavEyebrow, + SettingsNavItem, + SettingsNavSection, +} from '../common-settings/SettingsNav'; import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize'; import { useMatrixClient } from '../../hooks/useMatrixClient'; import { mxcUrlToHttp } from '../../utils/matrix'; @@ -63,6 +68,7 @@ type SpaceSettingsProps = { requestClose: () => void; }; export function SpaceSettings({ initialPage, requestClose }: SpaceSettingsProps) { + const { t } = useTranslation(); const room = useRoom(); const mx = useMatrixClient(); const useAuthentication = useMediaAuthentication(); @@ -98,8 +104,8 @@ export function SpaceSettings({ initialPage, requestClose }: SpaceSettingsProps) screenSize === ScreenSize.Mobile && activePage !== undefined ? undefined : ( - - + + - - {roomName} - + + {t('RoomSettings.settings')} + + {roomName} + + {screenSize === ScreenSize.Mobile && ( @@ -129,25 +138,15 @@ export function SpaceSettings({ initialPage, requestClose }: SpaceSettingsProps)
+ {t('RoomSettings.sections')} {menuItems.map((item) => ( - } + icon={item.icon} + label={item.name} + active={activePage === item.page} onClick={() => setActivePage(item.page)} - > - - {item.name} - - + /> ))}