🆙 Update the navigator

This commit is contained in:
duckietm
2026-02-26 10:25:04 +01:00
parent faadd0cf31
commit 7ab3b24331
49 changed files with 716 additions and 282 deletions
@@ -1,106 +1,196 @@
import { RoomDataParser } from '@nitrots/nitro-renderer';
import { FC, useRef, useState } from 'react';
import { RoomDataParser, RoomSettingsComposer, UpdateHomeRoomMessageComposer } from '@nitrots/nitro-renderer';
import React, { FC, useRef, useState } from 'react';
import { FaUser } from 'react-icons/fa';
import { ArrowContainer, Popover } from 'react-tiny-popover';
import { LocalizeText } from '../../../../api';
import { Flex, LayoutBadgeImageView, LayoutRoomThumbnailView, NitroCardContentView, Text, UserProfileIconView } from '../../../../common';
import { GetGroupInformation, GetSessionDataManager, GetUserProfile, LocalizeText, ReportType, SendMessageComposer, ToggleFavoriteRoom } from '../../../../api';
import { Column, Flex, LayoutBadgeImageView, LayoutRoomThumbnailView, NitroCardContentView, Text, UserProfileIconView } from '../../../../common';
import { useHelp, useNavigator } from '../../../../hooks';
import { classNames } from '../../../../layout';
export const NavigatorSearchResultItemInfoView: FC<{
roomData: RoomDataParser;
}> = props =>
interface NavigatorSearchResultItemInfoViewProps
{
const { roomData = null } = props;
const [ isVisible, setIsVisible ] = useState(false);
const elementRef = useRef<HTMLDivElement>();
roomData: RoomDataParser;
isVisible?: boolean;
onToggle?: (e: React.MouseEvent) => void;
setIsPopoverActive?: React.Dispatch<React.SetStateAction<boolean>>;
}
export const NavigatorSearchResultItemInfoView: FC<NavigatorSearchResultItemInfoViewProps> = props =>
{
const { roomData = null, isVisible = undefined, onToggle, setIsPopoverActive } = props;
const elementRef = useRef<HTMLDivElement>(null);
const [ internalVisible, setInternalVisible ] = useState(false);
const { navigatorData = null, favouriteRoomIds = [] } = useNavigator();
const { report = null } = useHelp();
const isControlled = isVisible !== undefined;
const popoverOpen = isControlled ? isVisible : internalVisible;
const getUserCounterColor = () =>
{
const num: number = (100 * (roomData.userCount / roomData.maxUserCount));
let bg = 'bg-primary';
if(num >= 92) return 'bg-danger';
if(num >= 50) return 'bg-warning';
if(num > 0) return 'bg-success';
if(num >= 92)
{
bg = 'bg-danger';
}
else if(num >= 50)
{
bg = 'bg-warning';
}
else if(num > 0)
{
bg = 'bg-success';
}
return bg;
return 'bg-primary';
};
function dispatch(arg0: string): void
const processAction = (action: string) =>
{
throw new Error('Function not implemented.');
}
if(!navigatorData || !roomData) return;
switch(action)
{
case 'set_home_room':
{
let newRoomId = -1;
if(navigatorData.homeRoomId !== roomData.roomId) newRoomId = roomData.roomId;
if(newRoomId > 0) SendMessageComposer(new UpdateHomeRoomMessageComposer(newRoomId));
return;
}
case 'open_room_settings':
SendMessageComposer(new RoomSettingsComposer(roomData.roomId));
return;
case 'report_room':
report(ReportType.ROOM, { roomId: roomData.roomId, roomName: roomData.roomName });
return;
case 'room_favourite':
ToggleFavoriteRoom(roomData.roomId, favouriteRoomIds.includes(roomData.roomId));
return;
}
};
const handleOwnerClick = (e: React.MouseEvent) =>
{
e.stopPropagation();
GetUserProfile(roomData.ownerId);
};
const handleGroupClick = (e: React.MouseEvent) =>
{
e.stopPropagation();
GetGroupInformation(roomData.habboGroupId);
};
const getTradeModeText = (): string =>
{
if(roomData.tradeMode === 1) return LocalizeText('trading.mode.free');
return LocalizeText('trading.mode.not.allowed');
};
const handleIconClick = (e: React.MouseEvent) =>
{
e.preventDefault();
e.stopPropagation();
if(onToggle) onToggle(e);
};
return (
<>
<Popover
ref={ elementRef } // if you'd like a ref to your popover's child, you can grab one here
containerClassName="max-w-[276px] not-italic font-normal leading-normal text-left no-underline text-shadow-none normal-case tracking-[normal] [word-break:normal] [word-spacing:normal] whitespace-normal text-[.7875rem] [word-wrap:break-word] bg-[#dfdfdf] bg-clip-padding border border-[solid] border-[#283F5D] rounded-[.25rem] [box-shadow:0_2px_#00000073] z-1070"
content={ ({ position, childRect, popoverRect }) => (
<ArrowContainer // if you'd like an arrow, you can import the ArrowContainer!
arrowColor={ 'black' }
arrowSize={ 7 }
arrowStyle={ { left: 'calc(-.5rem - 0px)' } }
childRect={ childRect }
popoverRect={ popoverRect }
position={ position }
>
<NitroCardContentView className="bg-transparent room-info image-rendering-pixelated" overflow="hidden">
<Flex gap={ 2 } overflow="hidden">
<LayoutRoomThumbnailView className="flex flex-col items-center mb-1 justify-content-end" customUrl={ roomData.officialRoomPicRef } roomId={ roomData.roomId }>
{ roomData.habboGroupId > 0 && (
<LayoutBadgeImageView badgeCode={ roomData.groupBadgeCode } className={ 'absolute top-0 inset-s-0 m-1 ' } isGroup={ true } />) }
{ roomData.doorMode !== RoomDataParser.OPEN_STATE && (
<i className={ 'absolute inset-e-0 mb-1 me-1 icon icon-navigator-room-' + (roomData.doorMode === RoomDataParser.DOORBELL_STATE ? 'locked' : roomData.doorMode === RoomDataParser.PASSWORD_STATE ? 'password' : roomData.doorMode === RoomDataParser.INVISIBLE_STATE ? 'invisible' : '') } />) }
</LayoutRoomThumbnailView>
<div className="flex flex-col gap-1">
<Text bold truncate className="grow" style={ { maxHeight: 13 } }>
{ roomData.roomName }
</Text>
<div className="flex gap-2">
<Text italics variant="muted">
{ LocalizeText('navigator.roomownercaption') }
</Text>
<div className="flex items-center gap-1">
<UserProfileIconView userId={ roomData.ownerId } />
<Text italics>{ roomData.ownerName }</Text>
</div>
</div>
<Text className="grow">
{ roomData.description }
</Text>
<Flex className={ 'badge p-1 absolute m-1 bottom-0 inset-e-0 m-2 ' + getUserCounterColor() } gap={ 1 }>
<FaUser className="fa-icon" />
{ roomData.userCount }
</Flex>
</div>
<Popover
containerClassName="max-w-[276px] not-italic font-normal leading-normal text-left no-underline text-shadow-none normal-case tracking-[normal] [word-break:normal] [word-spacing:normal] whitespace-normal text-[.7875rem] [word-wrap:break-word] bg-[#dfdfdf] bg-clip-padding border border-[solid] border-[#283F5D] rounded-[.25rem] [box-shadow:0_2px_#00000073] z-[1070]"
content={ ({ position, childRect, popoverRect }) => (
<ArrowContainer
arrowColor="black"
arrowSize={ 7 }
arrowStyle={ { left: 'calc(-.5rem - 0px)' } }
childRect={ childRect }
popoverRect={ popoverRect }
position={ position }
>
<NitroCardContentView className="bg-transparent room-info image-rendering-pixelated" overflow="hidden" onClick={ e => e.stopPropagation() }>
<Flex gap={ 1 } overflow="hidden" className="p-2">
<LayoutRoomThumbnailView className="flex flex-col items-center justify-end mb-1" customUrl={ roomData.officialRoomPicRef } roomId={ roomData.roomId }>
{ roomData.habboGroupId > 0 && (
<LayoutBadgeImageView badgeCode={ roomData.groupBadgeCode } className="absolute top-0 inset-s-0 m-1" isGroup={ true } />) }
{ roomData.doorMode !== RoomDataParser.OPEN_STATE && (
<i className={ 'absolute inset-e-0 mb-1 me-1 icon icon-navigator-room-' + (roomData.doorMode === RoomDataParser.DOORBELL_STATE ? 'locked' : roomData.doorMode === RoomDataParser.PASSWORD_STATE ? 'password' : roomData.doorMode === RoomDataParser.INVISIBLE_STATE ? 'invisible' : '') } />) }
</LayoutRoomThumbnailView>
<Column gap={ 1 }>
<Text bold className="grow" style={ { maxHeight: 13 } }>
{ roomData.roomName.length > 35 ? roomData.roomName.substring(0, 35) + '…' : roomData.roomName }
</Text>
<Text className="grow text-xs">{ roomData.description }</Text>
</Column>
</Flex>
<Column gap={ 0 } className="px-2 pb-2">
<Flex alignItems="center" className="mb-2">
{ roomData.ownerName && roomData.ownerName.length > 0 &&
<Flex onClick={ handleOwnerClick } gap={ 1 } className="w-1/2 items-center cursor-pointer">
<UserProfileIconView userId={ roomData.ownerId } />
<Text pointer bold underline>{ roomData.ownerName }</Text>
</Flex> }
{ roomData.habboGroupId > 0 &&
<Flex onClick={ handleGroupClick } gap={ 1 } className="w-1/2 items-center cursor-pointer">
<i className="icon icon-navigator-room-group" />
<Text bold underline className="truncate" style={ { maxWidth: 100 } }>{ roomData.groupName }</Text>
</Flex> }
</Flex>
</NitroCardContentView>
</ArrowContainer>
) }
isOpen={ isVisible }
padding={ 10 }
positions={ [ 'right' ] }
>
<div ref={ elementRef } className="cursor-pointer nitro-icon icon-navigator-info" onMouseLeave={ event => setIsVisible(false) } onMouseOver={ event => setIsVisible(true) } />
</Popover>
</>
<Flex gap={ 4 } className="w-full">
<Column className="w-3/5" gap={ 1 }>
<Flex gap={ 2 } alignItems="center">
<Text bold className="text-xs">{ LocalizeText('navigator.roompopup.property.trading') }</Text>
<Text className="text-xs">{ getTradeModeText() }</Text>
</Flex>
<Flex gap={ 2 } alignItems="center">
<Text bold className="text-xs">{ LocalizeText('navigator.roompopup.property.max_users') }</Text>
<Text className="text-xs">{ roomData.maxUserCount }</Text>
</Flex>
<Flex gap={ 1 } alignItems="center">
<Flex center className={ 'rounded px-1 py-0.5 text-xs font-bold text-white ' + getUserCounterColor() } gap={ 1 }>
<FaUser className="fa-icon" />
{ roomData.userCount }
</Flex>
</Flex>
</Column>
<Column alignItems="start" gap={ 2 } className="w-2/5">
<Flex pointer alignItems="center" gap={ 2 } onClick={ () => processAction('room_favourite') }>
<i className={ classNames('icon icon-navigator-favorite-room', favouriteRoomIds.includes(roomData.roomId) ? 'active' : '') } />
<Text className="text-xs">{ LocalizeText('navigator.room.popup.room.info.favorite') }</Text>
</Flex>
<Flex pointer alignItems="center" gap={ 2 } onClick={ () => processAction('set_home_room') }>
<i className={ classNames('icon icon-navigator-my-room', (navigatorData?.homeRoomId !== roomData.roomId) ? '' : 'active') } />
<Text className="text-xs">{ LocalizeText('navigator.room.popup.room.info.home') }</Text>
</Flex>
{ GetSessionDataManager().userId === roomData.ownerId &&
<Flex pointer alignItems="center" gap={ 2 } onClick={ () => processAction('open_room_settings') }>
<i className="icon icon-navigator-room-settings" />
<Text className="text-xs">{ LocalizeText('navigator.room.popup.info.room.settings') }</Text>
</Flex> }
{ GetSessionDataManager().userId !== roomData.ownerId &&
<Flex pointer alignItems="center" gap={ 2 } onClick={ () => processAction('report_room') }>
<i className="icon icon-navigator-room-report" />
<Text className="text-xs">{ LocalizeText('navigator.room.popup.report.room') }</Text>
</Flex> }
</Column>
</Flex>
{ roomData.tags && roomData.tags.length > 0 &&
<Flex gap={ 1 } className="mt-1">
{ roomData.tags.map((tag, i) => (
<Text key={ i } variant="white" className="bg-orange-500 px-1 rounded text-xs">#{ tag }</Text>
)) }
</Flex> }
</Column>
</NitroCardContentView>
</ArrowContainer>
) }
isOpen={ popoverOpen }
onClickOutside={ () =>
{
if(!isControlled) setInternalVisible(false);
if(setIsPopoverActive) setIsPopoverActive(false);
} }
padding={ 10 }
positions={ [ 'right', 'left', 'top', 'bottom' ] }
>
<div
ref={ elementRef }
className="cursor-pointer nitro-icon icon-navigator-info"
onClick={ handleIconClick }
onMouseOver={ () => { if(!isControlled) setInternalVisible(true); } }
onMouseLeave={ () => { if(!isControlled) setInternalVisible(false); } }
/>
</Popover>
);
};
@@ -1,5 +1,5 @@
import { GetSessionDataManager, RoomDataParser } from '@nitrots/nitro-renderer';
import { FC, MouseEvent } from 'react';
import React, { FC, MouseEvent, useEffect } from 'react';
import { FaUser } from 'react-icons/fa';
import { CreateRoomSession, DoorStateType, TryVisitRoom } from '../../../../api';
import { Column, Flex, LayoutBadgeImageView, LayoutGridItemProps, LayoutRoomThumbnailView, Text } from '../../../../common';
@@ -8,35 +8,84 @@ import { NavigatorSearchResultItemInfoView } from './NavigatorSearchResultItemIn
export interface NavigatorSearchResultItemViewProps extends LayoutGridItemProps
{
roomData: RoomDataParser
thumbnail?: boolean
roomData: RoomDataParser;
thumbnail?: boolean;
selectedRoomId?: number | null;
setSelectedRoomId?: React.Dispatch<React.SetStateAction<number | null>>;
isPopoverActive?: boolean;
setIsPopoverActive?: React.Dispatch<React.SetStateAction<boolean>>;
}
export const NavigatorSearchResultItemView: FC<NavigatorSearchResultItemViewProps> = props =>
{
const { roomData = null, children = null, thumbnail = false, ...rest } = props;
const { roomData = null, children = null, thumbnail = false, selectedRoomId, setSelectedRoomId, isPopoverActive, setIsPopoverActive, ...rest } = props;
const { setDoorData = null } = useNavigator();
const handleMouseEnter = () =>
{
if(isPopoverActive && setSelectedRoomId) setSelectedRoomId(roomData.roomId);
};
const handleMouseLeave = () =>
{
if(setSelectedRoomId && setIsPopoverActive)
{
setSelectedRoomId(null);
setIsPopoverActive(false);
}
};
const handleInfoClick = (e: React.MouseEvent) =>
{
e.preventDefault();
e.stopPropagation();
if(setIsPopoverActive && setSelectedRoomId)
{
if(!isPopoverActive)
{
setSelectedRoomId(roomData.roomId);
setIsPopoverActive(true);
}
else if(selectedRoomId === roomData.roomId)
{
setSelectedRoomId(null);
setIsPopoverActive(false);
}
else
{
setSelectedRoomId(roomData.roomId);
}
}
};
useEffect(() =>
{
const handleClickOutside = (event: Event) =>
{
const target = event.target as HTMLElement;
const navigatorItem = target.closest('.navigator-item');
if(!navigatorItem && setIsPopoverActive && setSelectedRoomId)
{
setIsPopoverActive(false);
setSelectedRoomId(null);
}
};
document.addEventListener('click', handleClickOutside);
return () => document.removeEventListener('click', handleClickOutside);
}, [ setIsPopoverActive, setSelectedRoomId ]);
const getUserCounterColor = () =>
{
const num: number = (100 * (roomData.userCount / roomData.maxUserCount));
let bg = 'bg-primary';
if(num >= 92) return 'bg-danger';
if(num >= 50) return 'bg-warning';
if(num > 0) return 'bg-success';
if(num >= 92)
{
bg = 'bg-danger';
}
else if(num >= 50)
{
bg = 'bg-warning';
}
else if(num > 0)
{
bg = 'bg-success';
}
return bg;
return 'bg-primary';
};
const visitRoom = (event: MouseEvent) =>
@@ -46,7 +95,6 @@ export const NavigatorSearchResultItemView: FC<NavigatorSearchResultItemViewProp
if(roomData.habboGroupId !== 0)
{
TryVisitRoom(roomData.roomId);
return;
}
@@ -56,10 +104,8 @@ export const NavigatorSearchResultItemView: FC<NavigatorSearchResultItemViewProp
setDoorData(prevValue =>
{
const newValue = { ...prevValue };
newValue.roomInfo = roomData;
newValue.state = DoorStateType.START_DOORBELL;
return newValue;
});
return;
@@ -67,10 +113,8 @@ export const NavigatorSearchResultItemView: FC<NavigatorSearchResultItemViewProp
setDoorData(prevValue =>
{
const newValue = { ...prevValue };
newValue.roomInfo = roomData;
newValue.state = DoorStateType.START_PASSWORD;
return newValue;
});
return;
@@ -81,10 +125,20 @@ export const NavigatorSearchResultItemView: FC<NavigatorSearchResultItemViewProp
};
if(thumbnail) return (
<Column pointer alignItems="center" className="navigator-item p-1 bg-light rounded-3 small mb-1 flex-col border border-muted" gap={ 0 } overflow="hidden" onClick={ visitRoom } { ...rest }>
<Column
pointer
overflow="hidden"
alignItems="center"
className="navigator-item p-1 bg-light rounded-3 small mb-1 flex-col border border-muted"
gap={ 0 }
onClick={ visitRoom }
onMouseEnter={ handleMouseEnter }
onMouseLeave={ handleMouseLeave }
{ ...rest }
>
<LayoutRoomThumbnailView className="flex flex-col items-center justify-end mb-1" customUrl={ roomData.officialRoomPicRef } roomId={ roomData.roomId }>
{ roomData.habboGroupId > 0 && <LayoutBadgeImageView badgeCode={ roomData.groupBadgeCode } className={ 'absolute top-0 inset-s-0 m-1' } isGroup={ true } /> }
<Flex center className={ 'inline-block px-[.65em] py-[.35em] text-[.75em] font-bold leading-none text-[#fff] text-center whitespace-nowrap align-baseline rounded-[.25rem] p-1 absolute m-1 ' + getUserCounterColor() } gap={ 1 }>
{ roomData.habboGroupId > 0 && <LayoutBadgeImageView badgeCode={ roomData.groupBadgeCode } className="absolute top-0 inset-s-0 m-1" isGroup={ true } /> }
<Flex center className={ 'inline-block px-[.65em] py-[.35em] text-[.75em] font-bold leading-none text-white text-center whitespace-nowrap align-baseline rounded-[.25rem] p-1 absolute m-1 ' + getUserCounterColor() } gap={ 1 }>
<FaUser className="fa-icon" />
{ roomData.userCount }
</Flex>
@@ -94,23 +148,42 @@ export const NavigatorSearchResultItemView: FC<NavigatorSearchResultItemViewProp
<Flex className="w-full">
<Text truncate className="grow!">{ roomData.roomName }</Text>
<Flex reverse alignItems="center" gap={ 1 }>
<NavigatorSearchResultItemInfoView roomData={ roomData } />
<NavigatorSearchResultItemInfoView
isVisible={ selectedRoomId === roomData.roomId }
onToggle={ handleInfoClick }
setIsPopoverActive={ setIsPopoverActive }
roomData={ roomData }
/>
</Flex>
{ children }
</Flex>
</Column>
);
return (
<Flex pointer alignItems="center" className="navigator-item px-2 py-1 small" gap={ 2 } overflow="hidden" onClick={ visitRoom } { ...rest }>
<Flex center className={ 'inline-block px-[.65em] py-[.35em] text-[.75em] font-bold leading-none text-[#fff] text-center whitespace-nowrap align-baseline rounded-[.25rem] p-1 ' + getUserCounterColor() } gap={ 1 }>
<Flex
pointer
alignItems="center"
className="navigator-item px-2 py-1 small"
gap={ 2 }
overflow="hidden"
onClick={ visitRoom }
onMouseEnter={ handleMouseEnter }
onMouseLeave={ handleMouseLeave }
{ ...rest }
>
<Flex center className={ 'inline-block px-[.65em] py-[.35em] text-[.75em] font-bold leading-none text-white text-center whitespace-nowrap align-baseline rounded-[.25rem] p-1 ' + getUserCounterColor() } gap={ 1 }>
<FaUser className="fa-icon" />
{ roomData.userCount }
</Flex>
<Text grow truncate>{ roomData.roomName }</Text>
<Flex reverse alignItems="center" gap={ 1 }>
<NavigatorSearchResultItemInfoView roomData={ roomData } />
<Text grow truncate className="min-w-0">{ roomData.roomName }</Text>
<Flex reverse alignItems="center" gap={ 1 } className="shrink-0">
<NavigatorSearchResultItemInfoView
isVisible={ selectedRoomId === roomData.roomId && isPopoverActive }
onToggle={ handleInfoClick }
setIsPopoverActive={ setIsPopoverActive }
roomData={ roomData }
/>
{ roomData.habboGroupId > 0 && <i className="nitro-icon icon-navigator-room-group" /> }
{ (roomData.doorMode !== RoomDataParser.OPEN_STATE) &&
<i className={ ('nitro-icon icon-navigator-room-' + ((roomData.doorMode === RoomDataParser.DOORBELL_STATE) ? 'locked' : (roomData.doorMode === RoomDataParser.PASSWORD_STATE) ? 'password' : (roomData.doorMode === RoomDataParser.INVISIBLE_STATE) ? 'invisible' : '')) } /> }
@@ -1,8 +1,8 @@
import { NavigatorSearchComposer, NavigatorSearchResultList } from '@nitrots/nitro-renderer';
import { NavigatorSearchComposer, NavigatorSearchResultList, NavigatorSearchSaveComposer } from '@nitrots/nitro-renderer';
import { FC, useEffect, useState } from 'react';
import { FaBars, FaMinus, FaPlus, FaTh, FaWindowMaximize, FaWindowRestore } from 'react-icons/fa';
import { LocalizeText, NavigatorSearchResultViewDisplayMode, SendMessageComposer } from '../../../../api';
import { AutoGrid, AutoGridProps, Column, Flex, Grid, Text } from '../../../../common';
import { AutoGrid, AutoGridProps, Column, Flex, Grid, LayoutSearchSavesView, Text } from '../../../../common';
import { useNavigator } from '../../../../hooks';
import { NavigatorSearchResultItemView } from './NavigatorSearchResultItemView';
@@ -16,6 +16,8 @@ export const NavigatorSearchResultView: FC<NavigatorSearchResultViewProps> = pro
const { searchResult = null, ...rest } = props;
const [ isExtended, setIsExtended ] = useState(true);
const [ displayMode, setDisplayMode ] = useState<number>(0);
const [ selectedRoomId, setSelectedRoomId ] = useState<number | null>(null);
const [ isPopoverActive, setIsPopoverActive ] = useState<boolean>(false);
const { topLevelContext = null } = useNavigator();
@@ -23,7 +25,7 @@ export const NavigatorSearchResultView: FC<NavigatorSearchResultViewProps> = pro
{
let name = searchResult.code;
if(!name || !name.length || LocalizeText('navigator.searchcode.title.' + name) == ('navigator.searchcode.title.' + name)) return searchResult.data;
if(!name || !name.length || LocalizeText('navigator.searchcode.title.' + name) === ('navigator.searchcode.title.' + name)) return searchResult.data;
if(name.startsWith('${')) return name.slice(2, (name.length - 1));
@@ -51,7 +53,6 @@ export const NavigatorSearchResultView: FC<NavigatorSearchResultViewProps> = pro
if(!searchResult) return;
setIsExtended(!searchResult.closed);
setDisplayMode(searchResult.mode);
}, [ searchResult ]);
@@ -65,54 +66,46 @@ export const NavigatorSearchResultView: FC<NavigatorSearchResultViewProps> = pro
{ !isExtended && <FaPlus className="text-secondary fa-icon" /> }
<Text>{ LocalizeText(getResultTitle()) }</Text>
</Flex>
<div className="flex gap-2">
{ (displayMode === NavigatorSearchResultViewDisplayMode.LIST) && <FaTh className="text-secondary fa-icon" onClick={ toggleDisplayMode } /> }
{ (displayMode >= NavigatorSearchResultViewDisplayMode.THUMBNAILS) && <FaBars className="text-secondary fa-icon" onClick={ toggleDisplayMode } /> }
{ (searchResult.action > 0) && (searchResult.action === 1) && <FaWindowMaximize className="text-secondary fa-icon" onClick={ showMore } /> }
{ (searchResult.action > 0) && (searchResult.action !== 1) && <FaWindowRestore className="text-secondary fa-icon" onClick={ showMore } /> }
<div className="flex gap-2 items-center">
{ (displayMode === NavigatorSearchResultViewDisplayMode.LIST) && <FaTh className="text-secondary fa-icon cursor-pointer" onClick={ toggleDisplayMode } /> }
{ (displayMode >= NavigatorSearchResultViewDisplayMode.THUMBNAILS) && <FaBars className="text-secondary fa-icon cursor-pointer" onClick={ toggleDisplayMode } /> }
{ (searchResult.action > 0) && (searchResult.action === 1) && <FaWindowMaximize className="text-secondary fa-icon cursor-pointer" title={ LocalizeText('navigator.more.rooms') } onClick={ showMore } /> }
{ (searchResult.action > 0) && (searchResult.action !== 1) && <FaWindowRestore className="text-secondary fa-icon cursor-pointer" title={ LocalizeText('navigator.back') } onClick={ showMore } /> }
<LayoutSearchSavesView
title={ LocalizeText('navigator.tooltip.add.saved.search') }
onClick={ () => SendMessageComposer(new NavigatorSearchSaveComposer(getResultTitle(), searchResult.data)) }
/>
</div>
</Flex> { isExtended &&
</Flex>
{ isExtended &&
<>
{
gridHasTwoColumns ? <AutoGrid columnCount={ 3 } { ...rest } className="mx-2" columnMinHeight={ 130 } columnMinWidth={ 110 }>
{ searchResult.rooms.length > 0 && searchResult.rooms.map((room, index) => <NavigatorSearchResultItemView key={ index } roomData={ room } thumbnail={ true } />) }
</AutoGrid> : <Grid className="navigator-grid" columnCount={ 1 } gap={ 0 }>
{ searchResult.rooms.length > 0 && searchResult.rooms.map((room, index) => <NavigatorSearchResultItemView key={ index } roomData={ room } />) }
</Grid>
}
</>
}
{ gridHasTwoColumns
? <AutoGrid columnCount={ 3 } { ...rest } className="mx-2" columnMinHeight={ 130 } columnMinWidth={ 110 }>
{ searchResult.rooms.length > 0 && searchResult.rooms.map((room, index) => (
<NavigatorSearchResultItemView
key={ index }
roomData={ room }
thumbnail={ true }
isPopoverActive={ isPopoverActive }
setIsPopoverActive={ setIsPopoverActive }
selectedRoomId={ selectedRoomId }
setSelectedRoomId={ setSelectedRoomId }
/>
)) }
</AutoGrid>
: <Grid className="navigator-grid" columnCount={ 1 } gap={ 0 }>
{ searchResult.rooms.length > 0 && searchResult.rooms.map((room, index) => (
<NavigatorSearchResultItemView
key={ index }
roomData={ room }
isPopoverActive={ isPopoverActive }
setIsPopoverActive={ setIsPopoverActive }
selectedRoomId={ selectedRoomId }
setSelectedRoomId={ setSelectedRoomId }
/>
)) }
</Grid> }
</> }
</Column>
// <div className="nitro-navigator-search-result bg-white rounded mb-2 overflow-hidden">
// <div className="flex flex-col">
// <div className="flex items-center px-2 py-1 text-black">
// <i className={ 'text-secondary fas ' + (isExtended ? 'fa-minus' : 'fa-plus') } onClick={ toggleExtended }></i>
// <div className="ms-2 grow!">{ LocalizeText(getResultTitle()) }</div>
// <i className={ 'text-secondary fas ' + classNames({ 'fa-bars': (displayMode === NavigatorSearchResultViewDisplayMode.LIST), 'fa-th': displayMode >= NavigatorSearchResultViewDisplayMode.THUMBNAILS })}></i>
// </div>
// { isExtended &&
// <div className={ 'nitro-navigator-result-list row row-cols-' + classNames({ '1': (displayMode === NavigatorSearchResultViewDisplayMode.LIST), '2': (displayMode >= NavigatorSearchResultViewDisplayMode.THUMBNAILS) }) }>
// { searchResult.rooms.length > 0 && searchResult.rooms.map((room, index) =>
// {
// return <NavigatorSearchResultItemView key={ index } roomData={ room } />
// }) }
// </div> }
// </div>
// </div>
// <div className="nitro-navigator-result-list p-2">
// <div className="flex mb-2 small cursor-pointer" onClick={ toggleList }>
// <i className={ "fas " + classNames({ 'fa-plus': !isExtended, 'fa-minus': isExtended })}></i>
// <div className="align-self-center w-full ml-2">{ LocalizeText(getListCode()) }</div>
// <i className={ "fas " + classNames({ 'fa-bars': displayMode === NavigatorResultListViewDisplayMode.LIST, 'fa-th': displayMode >= NavigatorResultListViewDisplayMode.THUMBNAILS })} onClick={ toggleDisplayMode }></i>
// </div>
// <div className={ 'row mr-n2 row-cols-' + classNames({ '1': displayMode === NavigatorResultListViewDisplayMode.LIST, '2': displayMode >= NavigatorResultListViewDisplayMode.THUMBNAILS }) }>
// { isExtended && resultList && resultList.rooms.map((room, index) =>
// {
// return <NavigatorResultView key={ index } result={ room } />
// })
// }
// </div>
// </div>
);
};
@@ -0,0 +1,46 @@
import { NavigatorDeleteSavedSearchComposer, NavigatorSavedSearch, NavigatorSearchComposer } from '@nitrots/nitro-renderer';
import { FC, useState } from 'react';
import { LocalizeText, SendMessageComposer } from '../../../../api';
import { Flex, Text } from '../../../../common';
export interface NavigatorSearchSavesResultItemViewProps
{
search: NavigatorSavedSearch;
}
export const NavigatorSearchSavesResultItemView: FC<NavigatorSearchSavesResultItemViewProps> = props =>
{
const { search = null } = props;
const [ isHovered, setIsHovered ] = useState(false);
const getResultTitle = () =>
{
let name = search.code;
if(!name || !name.length || LocalizeText('navigator.searchcode.title.' + name) === ('navigator.searchcode.title.' + name)) return search.code;
if(name.startsWith('${')) return name.slice(2, (name.length - 1));
return ('navigator.searchcode.title.' + name);
};
return (
<Flex grow pointer alignItems="center" gap={ 1 } onMouseEnter={ () => setIsHovered(true) } onMouseLeave={ () => setIsHovered(false) }>
{ isHovered &&
<i
className="nitro-icon icon-navigator-search-delete cursor-pointer flex-shrink-0"
title={ LocalizeText('navigator.tooltip.remove.saved.search') }
onClick={ () => SendMessageComposer(new NavigatorDeleteSavedSearchComposer(search.id)) }
/> }
<Text
small
pointer
variant="black"
title={ LocalizeText('navigator.tooltip.open.saved.search') }
onClick={ () => SendMessageComposer(new NavigatorSearchComposer(search.code.split('.').reverse()[0], search.filter)) }
>
{ LocalizeText(getResultTitle()) }
</Text>
</Flex>
);
};
@@ -0,0 +1,31 @@
import { NavigatorSavedSearch } from '@nitrots/nitro-renderer';
import { FC } from 'react';
import { FaBolt } from 'react-icons/fa';
import { LocalizeText } from '../../../../api';
import { Column, Flex, Text } from '../../../../common';
import { NavigatorSearchSavesResultItemView } from './NavigatorSearchSavesResultItemView';
export interface NavigatorSearchSavesResultViewProps
{
searches: NavigatorSavedSearch[];
}
export const NavigatorSearchSavesResultView: FC<NavigatorSearchSavesResultViewProps> = props =>
{
const { searches = [] } = props;
return (
<Column className="nitro-navigator-search-saves-result min-w-[100px]">
<Flex className="rounded px-2 py-1 bg-orange-500" gap={ 1 } alignItems="center">
<FaBolt color="white" />
<Text variant="white">{ LocalizeText('navigator.quick.links.title') }</Text>
</Flex>
<Column className="p-1 overflow-x-hidden overflow-y-auto">
{ (searches && searches.length > 0) &&
searches.map((search: NavigatorSavedSearch) => (
<NavigatorSearchSavesResultItemView key={ search.id } search={ search } />
)) }
</Column>
</Column>
);
};