style(navigator): unify room-settings tabs with section cards

Introduce a reusable NavigatorRoomSettingsSectionView card (rounded
bg-gray-100 panel with a bold-small title) and apply it across the
Access, VIP/Chat, Moderation and Rights tabs so every room-settings
screen matches the Base and Misc tab styling. Pure visual restyle —
handleChange wiring, events, composers and validations are unchanged.
This commit is contained in:
simoleo89
2026-05-31 09:20:35 +02:00
parent 5bc3c4ef34
commit c9df2d8765
5 changed files with 152 additions and 131 deletions
@@ -2,6 +2,7 @@ import { RoomDataParser } from '@nitrots/nitro-renderer';
import { FC, useEffect, useState } from 'react';
import { IRoomData, LocalizeText } from '../../../../api';
import { Column, Flex, Text } from '../../../../common';
import { NavigatorRoomSettingsSectionView } from './NavigatorRoomSettingsSectionView';
interface NavigatorRoomSettingsTabViewProps
{
@@ -36,9 +37,8 @@ export const NavigatorRoomSettingsAccessTabView: FC<NavigatorRoomSettingsTabView
<Text bold>{ LocalizeText('navigator.roomsettings.roomaccess.caption') }</Text>
<Text>{ LocalizeText('navigator.roomsettings.roomaccess.info') }</Text>
</Column>
<Column overflow="auto">
<Column gap={ 1 }>
<Text bold>{ LocalizeText('navigator.roomsettings.doormode') }</Text>
<Column overflow="auto" gap={ 2 }>
<NavigatorRoomSettingsSectionView title={ LocalizeText('navigator.roomsettings.doormode') } gap={ 1 }>
<Flex alignItems="center" gap={ 1 }>
<input className="form-check-input" type="radio" name="lockState" checked={ (roomData.lockState === RoomDataParser.OPEN_STATE) && !isTryingPassword } onChange={ event => handleChange('lock_state', RoomDataParser.OPEN_STATE) } />
<Text>{ LocalizeText('navigator.roomsettings.doormode.open') }</Text>
@@ -70,9 +70,8 @@ export const NavigatorRoomSettingsAccessTabView: FC<NavigatorRoomSettingsTabView
</Text> }
</Column> }
</Flex>
</Column>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('navigator.roomsettings.pets') }</Text>
</NavigatorRoomSettingsSectionView>
<NavigatorRoomSettingsSectionView title={ LocalizeText('navigator.roomsettings.pets') } gap={ 1 }>
<Flex alignItems="center" gap={ 1 }>
<input className="form-check-input" type="checkbox" checked={ roomData.allowPets } onChange={ event => handleChange('allow_pets', event.target.checked) } />
<Text>{ LocalizeText('navigator.roomsettings.allowpets') }</Text>
@@ -81,7 +80,7 @@ export const NavigatorRoomSettingsAccessTabView: FC<NavigatorRoomSettingsTabView
<input className="form-check-input" type="checkbox" checked={ roomData.allowPetsEat } onChange={ event => handleChange('allow_pets_eat', event.target.checked) } />
<Text>{ LocalizeText('navigator.roomsettings.allowfoodconsume') }</Text>
</Flex>
</Column>
</NavigatorRoomSettingsSectionView>
</Column>
</>
);
@@ -3,6 +3,7 @@ import { FC, useEffect, useState } from 'react';
import { IRoomData, LocalizeText, SendMessageComposer } from '../../../../api';
import { Button, Column, Flex, Grid, Text, UserProfileIconView } from '../../../../common';
import { useMessageEvent } from '../../../../hooks';
import { NavigatorRoomSettingsSectionView } from './NavigatorRoomSettingsSectionView';
interface NavigatorRoomSettingsTabViewProps
{
@@ -51,7 +52,7 @@ export const NavigatorRoomSettingsModTabView: FC<NavigatorRoomSettingsTabViewPro
return (
<Grid overflow="auto">
<Column size={ 6 }>
<Text bold>{ LocalizeText('navigator.roomsettings.moderation.banned.users') } ({ bannedUsers.length })</Text>
<NavigatorRoomSettingsSectionView title={ `${ LocalizeText('navigator.roomsettings.moderation.banned.users') } (${ bannedUsers.length })` } gap={ 1 } className="h-full">
<Flex overflow="hidden" className="nitro-card-panel list-container p-2">
<Column fullWidth overflow="auto" gap={ 1 }>
{ bannedUsers && (bannedUsers.length > 0) && bannedUsers.map((user, index) =>
@@ -68,11 +69,12 @@ export const NavigatorRoomSettingsModTabView: FC<NavigatorRoomSettingsTabViewPro
<Button disabled={ (selectedUserId <= 0) } onClick={ event => unBanUser(selectedUserId) }>
{ LocalizeText('navigator.roomsettings.moderation.unban') } { selectedUserId > 0 && bannedUsers.find(user => (user.userId === selectedUserId))?.userName }
</Button>
</NavigatorRoomSettingsSectionView>
</Column>
<Column size={ 6 }>
<NavigatorRoomSettingsSectionView title={ LocalizeText('navigator.roomsettings.moderation') } gap={ 2 } className="h-full">
<Column gap={ 1 }>
<Text bold>{ LocalizeText('navigator.roomsettings.moderation.mute.header') }</Text>
<Flex alignItems="center" gap={ 1 }>
<Text bold small>{ LocalizeText('navigator.roomsettings.moderation.mute.header') }</Text>
<select className="form-select form-select-sm" value={ roomData.moderationSettings.allowMute } onChange={ event => handleChange('moderation_mute', event.target.value) }>
<option value={ RoomModerationSettings.MODERATION_LEVEL_NONE }>
{ LocalizeText('navigator.roomsettings.moderation.none') }
@@ -81,11 +83,9 @@ export const NavigatorRoomSettingsModTabView: FC<NavigatorRoomSettingsTabViewPro
{ LocalizeText('navigator.roomsettings.moderation.rights') }
</option>
</select>
</Flex>
</Column>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('navigator.roomsettings.moderation.kick.header') }</Text>
<Flex alignItems="center" gap={ 1 }>
<Text bold small>{ LocalizeText('navigator.roomsettings.moderation.kick.header') }</Text>
<select className="form-select form-select-sm" value={ roomData.moderationSettings.allowKick } onChange={ event => handleChange('moderation_kick', event.target.value) }>
<option value={ RoomModerationSettings.MODERATION_LEVEL_NONE }>
{ LocalizeText('navigator.roomsettings.moderation.none') }
@@ -97,11 +97,9 @@ export const NavigatorRoomSettingsModTabView: FC<NavigatorRoomSettingsTabViewPro
{ LocalizeText('navigator.roomsettings.moderation.all') }
</option>
</select>
</Flex>
</Column>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('navigator.roomsettings.moderation.ban.header') }</Text>
<Flex alignItems="center" gap={ 1 }>
<Text bold small>{ LocalizeText('navigator.roomsettings.moderation.ban.header') }</Text>
<select className="form-select form-select-sm" value={ roomData.moderationSettings.allowBan } onChange={ event => handleChange('moderation_ban', event.target.value) }>
<option value={ RoomModerationSettings.MODERATION_LEVEL_NONE }>
{ LocalizeText('navigator.roomsettings.moderation.none') }
@@ -110,8 +108,8 @@ export const NavigatorRoomSettingsModTabView: FC<NavigatorRoomSettingsTabViewPro
{ LocalizeText('navigator.roomsettings.moderation.rights') }
</option>
</select>
</Flex>
</Column>
</NavigatorRoomSettingsSectionView>
</Column>
</Grid>
);
@@ -3,6 +3,7 @@ import { FC, useEffect, useRef, useState } from 'react';
import { IRoomData, LocalizeText, SendMessageComposer } from '../../../../api';
import { Button, Column, Flex, Grid, Text, UserProfileIconView } from '../../../../common';
import { useFriends, useMessageEvent } from '../../../../hooks';
import { NavigatorRoomSettingsSectionView } from './NavigatorRoomSettingsSectionView';
interface NavigatorRoomSettingsTabViewProps
{
@@ -105,17 +106,15 @@ export const NavigatorRoomSettingsRightsTabView: FC<NavigatorRoomSettingsTabView
return (
<Grid>
<Column size={ 6 }>
<Text bold>
{ LocalizeText(
<NavigatorRoomSettingsSectionView gap={ 1 } className="h-full"
title={ LocalizeText(
'navigator.flatctrls.userswithrights',
[ 'displayed', 'total' ],
[
filteredUsersWithRights.size.toString(),
filteredUsersWithRights.size.toString()
]
) }
</Text>
) }>
<Flex overflow="hidden" className="nitro-card-panel p-2 list-container">
<Column fullWidth overflow="auto" gap={ 1 }>
{ Array.from(filteredUsersWithRights.entries()).map(([ id, name ], index) =>
@@ -141,20 +140,19 @@ export const NavigatorRoomSettingsRightsTabView: FC<NavigatorRoomSettingsTabView
onClick={ () => roomData && guardedSend('removeAll', new RemoveAllRightsMessageComposer(roomData.roomId)) }>
{ LocalizeText('navigator.flatctrls.clear') }
</Button>
</NavigatorRoomSettingsSectionView>
</Column>
<Column size={ 6 }>
<Text bold>
{ LocalizeText(
<NavigatorRoomSettingsSectionView gap={ 1 } className="h-full"
title={ LocalizeText(
'navigator.flatctrls.friends',
[ 'displayed', 'total' ],
[
friendsWithoutRights.length.toString(),
allFriends.length.toString()
]
) }
</Text>
) }>
<Flex overflow="hidden" className="nitro-card-panel p-2 list-container">
<Column fullWidth overflow="auto" gap={ 1 }>
{ friendsWithoutRights.map((friend, index) =>
@@ -173,6 +171,7 @@ export const NavigatorRoomSettingsRightsTabView: FC<NavigatorRoomSettingsTabView
}) }
</Column>
</Flex>
</NavigatorRoomSettingsSectionView>
</Column>
</Grid>
);
@@ -0,0 +1,22 @@
import { FC, ReactNode } from 'react';
import { Column, Text } from '../../../../common';
interface NavigatorRoomSettingsSectionViewProps
{
title?: string;
gap?: 1 | 2 | 3;
className?: string;
children: ReactNode;
}
export const NavigatorRoomSettingsSectionView: FC<NavigatorRoomSettingsSectionViewProps> = props =>
{
const { title = null, gap = 2, className = '', children = null } = props;
return (
<Column gap={ gap } className={ `rounded bg-gray-100 p-3 ${ className }`.trim() }>
{ title && <Text bold small>{ title }</Text> }
{ children }
</Column>
);
};
@@ -3,6 +3,7 @@ import { FC, useEffect, useState } from 'react';
import { GetClubMemberLevel, IRoomData, LocalizeText } from '../../../../api';
import { Column, Grid, Text } from '../../../../common';
import { NitroInput } from '../../../../layout';
import { NavigatorRoomSettingsSectionView } from './NavigatorRoomSettingsSectionView';
interface NavigatorRoomSettingsTabViewProps
{
@@ -29,7 +30,7 @@ export const NavigatorRoomSettingsVipChatTabView: FC<NavigatorRoomSettingsTabVie
</div>
<Grid className={ !isHC ? 'opacity-50 pointer-events-none' : '' } overflow="auto">
<Column gap={ 1 } size={ 6 }>
<Text small bold>{ LocalizeText('navigator.roomsettings.chat_settings') }</Text>
<NavigatorRoomSettingsSectionView title={ LocalizeText('navigator.roomsettings.chat_settings') } gap={ 1 } className="h-full">
<Text small>{ LocalizeText('navigator.roomsettings.chat_settings.info') }</Text>
<select className="form-select form-select-sm" disabled={ !isHC } value={ roomData.chatSettings.mode } onChange={ event => handleChange('bubble_mode', event.target.value) }>
<option value={ RoomChatSettings.CHAT_MODE_FREE_FLOW }>{ LocalizeText('navigator.roomsettings.chat.mode.free.flow') }</option>
@@ -52,9 +53,10 @@ export const NavigatorRoomSettingsVipChatTabView: FC<NavigatorRoomSettingsTabVie
</select>
<Text small>{ LocalizeText('navigator.roomsettings.chat_settings.hearing.distance') }</Text>
<NitroInput className="form-control-sm" disabled={ !isHC } min="0" type="number" value={ chatDistance } onBlur={ event => handleChange('chat_distance', chatDistance) } onChange={ event => setChatDistance(event.target.valueAsNumber) } />
</NavigatorRoomSettingsSectionView>
</Column>
<Column gap={ 1 } size={ 6 }>
<Text small bold>{ LocalizeText('navigator.roomsettings.vip_settings') }</Text>
<NavigatorRoomSettingsSectionView title={ LocalizeText('navigator.roomsettings.vip_settings') } gap={ 1 } className="h-full">
<div className="flex items-center gap-1">
<input checked={ roomData.hideWalls } className="form-check-input" disabled={ !isHC } type="checkbox" onChange={ event => handleChange('hide_walls', event.target.checked) } />
<Text small>{ LocalizeText('navigator.roomsettings.hide_walls') }</Text>
@@ -71,6 +73,7 @@ export const NavigatorRoomSettingsVipChatTabView: FC<NavigatorRoomSettingsTabVie
<option value="-1">{ LocalizeText('navigator.roomsettings.floor_thickness.thin') }</option>
<option value="-2">{ LocalizeText('navigator.roomsettings.floor_thickness.thinnest') }</option>
</select>
</NavigatorRoomSettingsSectionView>
</Column>
</Grid>
</>