mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-12-03 18:26:28 +01:00
Continue layout changes
This commit is contained in:
parent
6010103b90
commit
f5d64fa2b4
@ -1,10 +1,10 @@
|
||||
import { GetAssetManager, GetAvatarRenderManager, GetCommunication, GetConfiguration, GetLocalizationManager, GetRoomCameraWidgetManager, GetRoomEngine, GetRoomSessionManager, GetSessionDataManager, GetSoundManager, GetStage, GetTexturePool, GetTicker, HabboWebTools, LegacyExternalInterface, LoadGameUrlEvent, NitroLogger, NitroVersion, PrepareRenderer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { GetUIVersion } from './api';
|
||||
import { classNames } from './common';
|
||||
import { MainView } from './components/MainView';
|
||||
import { LoadingView } from './components/loading/LoadingView';
|
||||
import { useMessageEvent } from './hooks';
|
||||
import { classNames } from './layout';
|
||||
|
||||
NitroVersion.UI_VERSION = GetUIVersion();
|
||||
|
||||
|
BIN
src/assets/images/ui/loading_icon.png
Normal file
BIN
src/assets/images/ui/loading_icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 164 B |
@ -1,4 +1,4 @@
|
||||
export * from '../layout/InfiniteGrid';
|
||||
|
||||
export * from './AutoGrid';
|
||||
export * from './Base';
|
||||
export * from './Button';
|
||||
@ -13,7 +13,6 @@ export * from './Text';
|
||||
export * from './card';
|
||||
export * from './card/accordion';
|
||||
export * from './card/tabs';
|
||||
export * from './classNames';
|
||||
export * from './draggable-window';
|
||||
export * from './layout';
|
||||
export * from './layout/limited-edition';
|
||||
|
@ -65,7 +65,7 @@ export const LayoutGridItem: FC<LayoutGridItemProps> = props =>
|
||||
{ (itemUniqueNumber > 0) &&
|
||||
<>
|
||||
<Base fit className="unique-bg-override" style={ { backgroundImage: `url(${ itemImage })` } } />
|
||||
<div className="position-absolute bottom-0 unique-item-counter">
|
||||
<div className="absolute bottom-0 unique-item-counter">
|
||||
<LayoutLimitedEditionStyledNumberView value={ itemUniqueNumber } />
|
||||
</div>
|
||||
</> }
|
||||
|
@ -80,8 +80,8 @@ export const LayoutRoomPreviewerView: FC<LayoutRoomPreviewerViewProps> = props =
|
||||
}, [ roomPreviewer, elementRef, height ]);
|
||||
|
||||
return (
|
||||
<div className="room-preview-container">
|
||||
<div ref={ elementRef } className="room-preview-image" style={ { height } } onClick={ onClick } />
|
||||
<div className="relative w-full">
|
||||
<div ref={ elementRef } className="rounded-md shadow" style={ { height } } onClick={ onClick } />
|
||||
{ children }
|
||||
</div>
|
||||
);
|
||||
|
@ -47,7 +47,7 @@ export const AchievementsView: FC<{}> = props =>
|
||||
<NitroCardView className="nitro-achievements" theme="primary-slim" uniqueKey="achievements">
|
||||
<NitroCardHeaderView headerText={ LocalizeText('inventory.achievements') } onCloseClick={ event => setIsVisible(false) } />
|
||||
{ selectedCategory &&
|
||||
<div className="position-relative gap-3 justify-center items-center cursor-pointer">
|
||||
<div className="relative gap-3 justify-center items-center cursor-pointer">
|
||||
<div className="nitro-achievements-back-arrow" onClick={ event => setSelectedCategoryCode(null) } />
|
||||
<Column className="flex-grow-1" gap={ 0 }>
|
||||
<Text className="text-small" fontSize={ 4 } fontWeight="bold">{ LocalizeText(`quests.${ selectedCategory.code }.name`) }</Text>
|
||||
|
@ -27,7 +27,7 @@ export const AvatarEditorFigurePreviewView: FC<{}> = props =>
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col figure-preview-container overflow-hidden position-relative">
|
||||
<div className="flex flex-col figure-preview-container overflow-hidden relative">
|
||||
<LayoutAvatarImageView direction={ direction } figure={ getFigureString } scale={ 2 } />
|
||||
<AvatarEditorIcon className="avatar-spotlight" icon="spotlight" />
|
||||
<div className="avatar-shadow" />
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { DetailedHTMLProps, HTMLAttributes, PropsWithChildren, forwardRef } from 'react';
|
||||
import { classNames } from '../../common';
|
||||
import { classNames } from '../../layout';
|
||||
|
||||
type AvatarIconType = 'male' | 'female' | 'clear' | 'sellable' | string;
|
||||
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { GetAvatarRenderManager, IAvatarFigureContainer, SaveWardrobeOutfitMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback } from 'react';
|
||||
import { GetClubMemberLevel, GetConfigurationValue, LocalizeText, SendMessageComposer } from '../../api';
|
||||
import { Button, InfiniteGrid, LayoutAvatarImageView, LayoutCurrencyIcon, LayoutGridItem } from '../../common';
|
||||
import { Button, LayoutAvatarImageView, LayoutCurrencyIcon } from '../../common';
|
||||
import { useAvatarEditor } from '../../hooks';
|
||||
import { InfiniteGrid } from '../../layout';
|
||||
|
||||
export const AvatarEditorWardrobeView: FC<{}> = props =>
|
||||
{
|
||||
@ -43,18 +44,18 @@ export const AvatarEditorWardrobeView: FC<{}> = props =>
|
||||
if(figureContainer) clubLevel = GetAvatarRenderManager().getFigureClubLevel(figureContainer, gender);
|
||||
|
||||
return (
|
||||
<LayoutGridItem className="nitro-avatar-editor-wardrobe-figure-preview" overflow="hidden" position="relative">
|
||||
<InfiniteGrid.Item className="nitro-avatar-editor-wardrobe-figure-preview">
|
||||
{ figureContainer &&
|
||||
<LayoutAvatarImageView direction={ 2 } figure={ figureContainer.getFigureString() } gender={ gender } /> }
|
||||
<div className="avatar-shadow" />
|
||||
{ !hcDisabled && (clubLevel > 0) && <LayoutCurrencyIcon className="position-absolute top-1 start-1" type="hc" /> }
|
||||
{ !hcDisabled && (clubLevel > 0) && <LayoutCurrencyIcon className="absolute top-1 start-1" type="hc" /> }
|
||||
<div className="flex gap-1 button-container">
|
||||
<Button fullWidth variant="link" onClick={ event => saveFigureAtWardrobeIndex(index) }>{ LocalizeText('avatareditor.wardrobe.save') }</Button>
|
||||
{ figureContainer &&
|
||||
<Button fullWidth disabled={ (clubLevel > GetClubMemberLevel()) } variant="link" onClick={ event => wearFigureAtIndex(index) }>{ LocalizeText('widget.generic_usable.button.use') }</Button> }
|
||||
</div>
|
||||
</LayoutGridItem>
|
||||
</InfiniteGrid.Item>
|
||||
)
|
||||
} } overscan={ 5 } rows={ savedFigures } />
|
||||
} } items={ savedFigures } overscan={ 5 } />
|
||||
);
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { AvatarFigurePartType } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { AvatarEditorThumbnailsHelper, GetConfigurationValue, IAvatarEditorCategoryPartItem } from '../../../api';
|
||||
import { LayoutCurrencyIcon, LayoutGridItem, LayoutGridItemProps } from '../../../common';
|
||||
import { LayoutCurrencyIcon, LayoutGridItemProps } from '../../../common';
|
||||
import { useAvatarEditor } from '../../../hooks';
|
||||
import { InfiniteGrid } from '../../../layout';
|
||||
import { AvatarEditorIcon } from '../AvatarEditorIcon';
|
||||
|
||||
export const AvatarEditorFigureSetItemView: FC<{
|
||||
@ -46,10 +47,10 @@ export const AvatarEditorFigureSetItemView: FC<{
|
||||
if(!partItem) return null;
|
||||
|
||||
return (
|
||||
<LayoutGridItem itemActive={ isSelected } itemImage={ (partItem.isClear ? undefined : assetUrl) } style={ { flex: '1', backgroundPosition: (setType === AvatarFigurePartType.HEAD) ? 'center -35px' : 'center' } } { ...rest }>
|
||||
{ !partItem.isClear && isHC && <LayoutCurrencyIcon className="position-absolute end-1 bottom-1" type="hc" /> }
|
||||
<InfiniteGrid.Item itemActive={ isSelected } itemImage={ (partItem.isClear ? undefined : assetUrl) } style={ { flex: '1', backgroundPosition: (setType === AvatarFigurePartType.HEAD) ? 'center -35px' : 'center' } } { ...rest }>
|
||||
{ !partItem.isClear && isHC && <LayoutCurrencyIcon className="absolute end-1 bottom-1" type="hc" /> }
|
||||
{ partItem.isClear && <AvatarEditorIcon icon="clear" /> }
|
||||
{ !partItem.isClear && partItem.partSet.isSellable && <AvatarEditorIcon className="end-1 bottom-1 position-absolute" icon="sellable" /> }
|
||||
</LayoutGridItem>
|
||||
{ !partItem.isClear && partItem.partSet.isSellable && <AvatarEditorIcon className="end-1 bottom-1 absolute" icon="sellable" /> }
|
||||
</InfiniteGrid.Item>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { FC } from 'react';
|
||||
import { IAvatarEditorCategory, IAvatarEditorCategoryPartItem } from '../../../api';
|
||||
import { InfiniteGrid } from '../../../common';
|
||||
import { useAvatarEditor } from '../../../hooks';
|
||||
import { InfiniteGrid } from '../../../layout';
|
||||
import { AvatarEditorFigureSetItemView } from './AvatarEditorFigureSetItemView';
|
||||
|
||||
export const AvatarEditorFigureSetView: FC<{
|
||||
@ -29,13 +29,13 @@ export const AvatarEditorFigureSetView: FC<{
|
||||
}
|
||||
|
||||
return (
|
||||
<InfiniteGrid columnCount={ columnCount } itemRender={ (item: IAvatarEditorCategoryPartItem) =>
|
||||
<InfiniteGrid<IAvatarEditorCategoryPartItem> columnCount={ columnCount } itemRender={ (item: IAvatarEditorCategoryPartItem) =>
|
||||
{
|
||||
if(!item) return null;
|
||||
|
||||
return (
|
||||
<AvatarEditorFigureSetItemView isSelected={ isPartItemSelected(item) } partItem={ item } setType={ category.setType } width={ `calc(100% / ${ columnCount }` } onClick={ event => selectEditorPart(category.setType, item.partSet?.id ?? -1) } />
|
||||
)
|
||||
} } overscan={ columnCount } rows={ category.partItems } />
|
||||
} } items={ category.partItems } overscan={ columnCount } />
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { ColorConverter, IPartColor } from '@nitrots/nitro-renderer';
|
||||
import { FC } from 'react';
|
||||
import { GetConfigurationValue } from '../../../api';
|
||||
import { LayoutCurrencyIcon, LayoutGridItem, LayoutGridItemProps } from '../../../common';
|
||||
import { LayoutCurrencyIcon, LayoutGridItemProps } from '../../../common';
|
||||
import { InfiniteGrid } from '../../../layout';
|
||||
|
||||
export const AvatarEditorPaletteSetItem: FC<{
|
||||
setType: string;
|
||||
@ -17,8 +18,8 @@ export const AvatarEditorPaletteSetItem: FC<{
|
||||
const isHC = !GetConfigurationValue<boolean>('hc.disabled', false) && (partColor.clubLevel > 0);
|
||||
|
||||
return (
|
||||
<LayoutGridItem itemHighlight className="clear-bg" itemActive={ isSelected } itemColor={ ColorConverter.int2rgb(partColor.rgb) } { ...rest }>
|
||||
{ isHC && <LayoutCurrencyIcon className="position-absolute end-1 bottom-1" type="hc" /> }
|
||||
</LayoutGridItem>
|
||||
<InfiniteGrid.Item itemHighlight className="clear-bg" itemActive={ isSelected } itemColor={ ColorConverter.int2rgb(partColor.rgb) } { ...rest }>
|
||||
{ isHC && <LayoutCurrencyIcon className="absolute end-1 bottom-1" type="hc" /> }
|
||||
</InfiniteGrid.Item>
|
||||
);
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { IPartColor } from '@nitrots/nitro-renderer';
|
||||
import { FC } from 'react';
|
||||
import { IAvatarEditorCategory } from '../../../api';
|
||||
import { InfiniteGrid } from '../../../common';
|
||||
import { useAvatarEditor } from '../../../hooks';
|
||||
import { InfiniteGrid } from '../../../layout';
|
||||
import { AvatarEditorPaletteSetItem } from './AvatarEditorPaletteSetItemView';
|
||||
|
||||
export const AvatarEditorPaletteSetView: FC<{
|
||||
@ -24,13 +24,13 @@ export const AvatarEditorPaletteSetView: FC<{
|
||||
}
|
||||
|
||||
return (
|
||||
<InfiniteGrid columnCount={ columnCount } itemRender={ (item: IPartColor) =>
|
||||
<InfiniteGrid<IPartColor> columnCount={ columnCount } itemRender={ (item: IPartColor) =>
|
||||
{
|
||||
if(!item) return null;
|
||||
|
||||
return (
|
||||
<AvatarEditorPaletteSetItem isSelected={ isPartColorSelected(item) } partColor={ item } setType={ category.setType } width={ `calc(100% / ${ columnCount }` } onClick={ event => selectEditorColor(category.setType, paletteIndex, item.id) } />
|
||||
)
|
||||
} } overscan={ columnCount } rows={ category.colorItems[paletteIndex] } />
|
||||
} } items={ category.colorItems[paletteIndex] } overscan={ columnCount } />
|
||||
);
|
||||
}
|
||||
|
@ -62,13 +62,13 @@ export const CameraWidgetCaptureView: FC<CameraWidgetCaptureViewProps> = props =
|
||||
<Column center className="nitro-camera-capture" gap={ 0 }>
|
||||
{ selectedPicture && <img alt="" className="camera-area" src={ selectedPicture.imageUrl } /> }
|
||||
<div className="camera-canvas drag-handler">
|
||||
<div className="position-absolute header-close" onClick={ onClose }>
|
||||
<div className="absolute header-close" onClick={ onClose }>
|
||||
<FaTimes className="fa-icon" />
|
||||
</div>
|
||||
{ !selectedPicture && <div ref={ elementRef } className="camera-area camera-view-finder" /> }
|
||||
{ selectedPicture &&
|
||||
<div className="camera-area camera-frame">
|
||||
<div className="camera-frame-preview-actions w-100 position-absolute bottom-0 py-2 text-center">
|
||||
<div className="camera-frame-preview-actions w-100 absolute bottom-0 py-2 text-center">
|
||||
<button className="btn btn-success me-3" title={ LocalizeText('camera.editor.button.tooltip') } onClick={ onEdit }>{ LocalizeText('camera.editor.button.text') }</button>
|
||||
<button className="btn btn-danger" onClick={ onDelete }>{ LocalizeText('camera.delete.button.text') }</button>
|
||||
</div>
|
||||
|
@ -2,9 +2,10 @@ import { GetSessionDataManager, GiftReceiverNotFoundEvent, PurchaseFromCatalogAs
|
||||
import { ChangeEvent, FC, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { FaChevronLeft, FaChevronRight } from 'react-icons/fa';
|
||||
import { ColorUtils, LocalizeText, MessengerFriend, ProductTypeEnum, SendMessageComposer } from '../../../../api';
|
||||
import { Button, Column, Flex, FormGroup, LayoutCurrencyIcon, LayoutFurniImageView, LayoutGiftTagView, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text, classNames } from '../../../../common';
|
||||
import { Button, Column, Flex, FormGroup, LayoutCurrencyIcon, LayoutFurniImageView, LayoutGiftTagView, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common';
|
||||
import { CatalogEvent, CatalogInitGiftEvent, CatalogPurchasedEvent } from '../../../../events';
|
||||
import { useCatalog, useFriends, useMessageEvent, useUiEvent } from '../../../../hooks';
|
||||
import { classNames } from '../../../../layout';
|
||||
|
||||
export const CatalogGiftView: FC<{}> = props =>
|
||||
{
|
||||
@ -275,7 +276,7 @@ export const CatalogGiftView: FC<{}> = props =>
|
||||
{ colors.map(color => <Button key={ color.id } active={ (color.id === selectedColorId) } disabled={ !isColorable } style={ { backgroundColor: color.color } } variant="dark" onClick={ () => setSelectedColorId(color.id) } />) }
|
||||
</div>
|
||||
</Column>
|
||||
<div className="flex justify-content-between items-center">
|
||||
<div className="flex items-center justify-content-between">
|
||||
<Button className="text-black" variant="link" onClick={ onClose }>
|
||||
{ LocalizeText('cancel') }
|
||||
</Button>
|
||||
|
@ -35,7 +35,7 @@ export const CatalogLayoutBadgeDisplayView: FC<CatalogLayoutProps> = props =>
|
||||
</> }
|
||||
{ currentOffer &&
|
||||
<>
|
||||
<div className="position-relative overflow-hidden">
|
||||
<div className="relative overflow-hidden">
|
||||
<CatalogViewProductWidgetView />
|
||||
</div>
|
||||
<Column className="flex-grow-1" gap={ 1 }>
|
||||
|
@ -150,7 +150,7 @@ export const CatalogLayoutColorGroupingView : FC<CatalogLayoutColorGroupViewProp
|
||||
</> }
|
||||
{ currentOffer &&
|
||||
<>
|
||||
<div className="position-relative overflow-hidden">
|
||||
<div className="relative overflow-hidden">
|
||||
<CatalogViewProductWidgetView />
|
||||
<CatalogAddOnBadgeWidgetView className="bg-muted rounded bottom-1 end-1" position="absolute" />
|
||||
{ currentOffer.product.furnitureData.hasIndexedColor &&
|
||||
|
@ -27,7 +27,7 @@ export const CatalogLayouGuildCustomFurniView: FC<CatalogLayoutProps> = props =>
|
||||
</> }
|
||||
{ currentOffer &&
|
||||
<>
|
||||
<div className="position-relative overflow-hidden">
|
||||
<div className="relative overflow-hidden">
|
||||
<CatalogViewProductWidgetView />
|
||||
<CatalogGuildBadgeWidgetView className="bottom-1 end-1" position="absolute" />
|
||||
</div>
|
||||
|
@ -30,7 +30,7 @@ export const CatalogLayoutSpacesView: FC<CatalogLayoutProps> = props =>
|
||||
</> }
|
||||
{ currentOffer &&
|
||||
<>
|
||||
<div className="position-relative overflow-hidden">
|
||||
<div className="relative overflow-hidden">
|
||||
<CatalogViewProductWidgetView />
|
||||
</div>
|
||||
<Column grow gap={ 1 }>
|
||||
|
@ -216,7 +216,7 @@ export const CatalogLayoutPetView: FC<CatalogLayoutProps> = props =>
|
||||
</> }
|
||||
{ currentOffer &&
|
||||
<>
|
||||
<div className="position-relative overflow-hidden">
|
||||
<div className="relative overflow-hidden">
|
||||
<CatalogViewProductWidgetView />
|
||||
<CatalogAddOnBadgeWidgetView className="bg-muted rounded bottom-1 end-1" position="absolute" />
|
||||
{ ((petIndex > -1) && (petIndex <= 7)) &&
|
||||
|
@ -45,7 +45,7 @@ export const OfferWindowView = (props: { offer: TargetedOfferData, setOpen: Disp
|
||||
|
||||
return <NitroCardView className="nitro-targeted-offer" theme="primary-slim" uniqueKey="targeted-offer">
|
||||
<NitroCardHeaderView headerText={ LocalizeText(offer.title) } onCloseClick={ event => setOpen(false) } />
|
||||
<div className="container-fluid p-1 position-relative justify-center items-center cursor-pointer gap-3 bg-danger">
|
||||
<div className="container-fluid p-1 relative justify-center items-center cursor-pointer gap-3 bg-danger">
|
||||
{ LocalizeText('targeted.offer.timeleft',[ 'timeleft' ],[ expirationTime() ]) }
|
||||
</div>
|
||||
<NitroCardContentView gap={ 1 }>
|
||||
@ -68,7 +68,7 @@ export const OfferWindowView = (props: { offer: TargetedOfferData, setOpen: Disp
|
||||
</Flex>
|
||||
<div className="w-50 h-100" style={ { background: `url(${ GetConfigurationValue('image.library.url') + offer.imageUrl }) no-repeat center` } } />
|
||||
</Flex>
|
||||
<Flex column alignItems="center" className="price-ray position-absolute" justifyContent="center">
|
||||
<Flex column alignItems="center" className="price-ray absolute" justifyContent="center">
|
||||
<Text>{ LocalizeText('targeted.offer.price.label') }</Text>
|
||||
{ offer.priceInCredits > 0 &&
|
||||
<div className="flex gap-1">
|
||||
|
@ -34,7 +34,7 @@ export const FriendBarItemView: FC<{ friend: MessengerFriend }> = props =>
|
||||
{
|
||||
return (
|
||||
<div ref={ elementRef } className="btn btn-primary friend-bar-item friend-bar-search">
|
||||
<div className="friend-bar-item-head position-absolute"/>
|
||||
<div className="friend-bar-item-head absolute"/>
|
||||
<div className="text-truncate">{ LocalizeText('friend.bar.find.title') }</div>
|
||||
</div>
|
||||
);
|
||||
@ -42,7 +42,7 @@ export const FriendBarItemView: FC<{ friend: MessengerFriend }> = props =>
|
||||
|
||||
return (
|
||||
<div ref={ elementRef } className={ 'btn btn-success friend-bar-item ' + (isVisible ? 'friend-bar-item-active' : '') } onClick={ event => setVisible(prevValue => !prevValue) }>
|
||||
<div className={ `friend-bar-item-head position-absolute ${ friend.id > 0 ? 'avatar': 'group' }` }>
|
||||
<div className={ `friend-bar-item-head absolute ${ friend.id > 0 ? 'avatar': 'group' }` }>
|
||||
{ (friend.id > 0) && <LayoutAvatarImageView direction={ 2 } figure={ friend.figure } headOnly={ true } /> }
|
||||
{ (friend.id <= 0) && <LayoutBadgeImageView badgeCode={ friend.figure } isGroup={ true } /> }
|
||||
</div>
|
||||
|
@ -2,8 +2,9 @@ import { AddLinkEventTracker, GetSessionDataManager, GroupAdminGiveComposer, Gro
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { FaChevronLeft, FaChevronRight } from 'react-icons/fa';
|
||||
import { GetUserProfile, LocalizeText, SendMessageComposer } from '../../../api';
|
||||
import { Button, classNames, Column, Flex, Grid, LayoutAvatarImageView, LayoutBadgeImageView, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../common';
|
||||
import { Button, Column, Flex, Grid, LayoutAvatarImageView, LayoutBadgeImageView, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../common';
|
||||
import { useMessageEvent, useNotification } from '../../../hooks';
|
||||
import { classNames } from '../../../layout';
|
||||
|
||||
export const GroupMembersView: FC<{}> = props =>
|
||||
{
|
||||
@ -166,8 +167,8 @@ export const GroupMembersView: FC<{}> = props =>
|
||||
{ membersData.result.map((member, index) =>
|
||||
{
|
||||
return (
|
||||
<Flex key={ index } alignItems="center" className="member-list-item bg-white rounded p-2" gap={ 2 } overflow="hidden">
|
||||
<div className="avatar-head cursor-pointer" onClick={ () => GetUserProfile(member.id) }>
|
||||
<Flex key={ index } alignItems="center" className="p-2 bg-white rounded member-list-item" gap={ 2 } overflow="hidden">
|
||||
<div className="cursor-pointer avatar-head" onClick={ () => GetUserProfile(member.id) }>
|
||||
<LayoutAvatarImageView direction={ 2 } figure={ member.figure } headOnly={ true } />
|
||||
</div>
|
||||
<Column grow gap={ 1 }>
|
||||
@ -182,11 +183,11 @@ export const GroupMembersView: FC<{}> = props =>
|
||||
</div> }
|
||||
{ membersData.admin && (member.rank === GroupRank.REQUESTED) &&
|
||||
<Flex alignItems="center">
|
||||
<div className="nitro-friends-spritesheet icon-accept cursor-pointer" title={ LocalizeText('group.members.accept') } onClick={ event => acceptMembership(member) } />
|
||||
<div className="cursor-pointer nitro-friends-spritesheet icon-accept" title={ LocalizeText('group.members.accept') } onClick={ event => acceptMembership(member) } />
|
||||
</Flex> }
|
||||
{ membersData.admin && (member.rank !== GroupRank.OWNER) && (member.id !== GetSessionDataManager().userId) &&
|
||||
<Flex alignItems="center">
|
||||
<div className="nitro-friends-spritesheet icon-deny cursor-pointer" title={ LocalizeText(member.rank === GroupRank.REQUESTED ? 'group.members.reject' : 'group.members.kick') } onClick={ event => removeMemberOrDeclineMembership(member) } />
|
||||
<div className="cursor-pointer nitro-friends-spritesheet icon-deny" title={ LocalizeText(member.rank === GroupRank.REQUESTED ? 'group.members.reject' : 'group.members.kick') } onClick={ event => removeMemberOrDeclineMembership(member) } />
|
||||
</Flex> }
|
||||
</div>
|
||||
</Flex>
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { GroupSaveColorsComposer } from '@nitrots/nitro-renderer';
|
||||
import { Dispatch, FC, SetStateAction, useCallback, useEffect, useState } from 'react';
|
||||
import { IGroupData, LocalizeText, SendMessageComposer } from '../../../../api';
|
||||
import { AutoGrid, Column, Grid, Text, classNames } from '../../../../common';
|
||||
import { AutoGrid, Column, Grid, Text } from '../../../../common';
|
||||
import { useGroup } from '../../../../hooks';
|
||||
import { classNames } from '../../../../layout';
|
||||
|
||||
interface GroupTabColorsViewProps
|
||||
{
|
||||
@ -99,7 +100,7 @@ export const GroupTabColorsView: FC<GroupTabColorsViewProps> = props =>
|
||||
<Column gap={ 1 } size={ 2 }>
|
||||
<Text bold>{ LocalizeText('group.edit.color.guild.color') }</Text>
|
||||
{ groupData.groupColors && (groupData.groupColors.length > 0) &&
|
||||
<div className="flex overflow-hidden rounded border">
|
||||
<div className="flex overflow-hidden border rounded">
|
||||
<div className="group-color-swatch" style={ { backgroundColor: '#' + getGroupColor(0) } } />
|
||||
<div className="group-color-swatch" style={ { backgroundColor: '#' + getGroupColor(1) } } />
|
||||
</div> }
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { GetSessionDataManager, GuideSessionGetRequesterRoomMessageComposer, GuideSessionInviteRequesterMessageComposer, GuideSessionMessageMessageComposer, GuideSessionRequesterRoomMessageEvent, GuideSessionResolvedMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, KeyboardEvent, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { GuideToolMessageGroup, LocalizeText, SendMessageComposer, TryVisitRoom } from '../../../api';
|
||||
import { Button, Column, Flex, LayoutAvatarImageView, Text, classNames } from '../../../common';
|
||||
import { Button, Column, Flex, LayoutAvatarImageView, Text } from '../../../common';
|
||||
import { useMessageEvent } from '../../../hooks';
|
||||
import { classNames } from '../../../layout';
|
||||
|
||||
interface GuideToolOngoingViewProps
|
||||
{
|
||||
@ -72,7 +73,7 @@ export const GuideToolOngoingView: FC<GuideToolOngoingViewProps> = props =>
|
||||
|
||||
return (
|
||||
<Column fullHeight>
|
||||
<Flex alignItems="center" className="bg-muted p-2 rounded" gap={ 1 } justifyContent="between">
|
||||
<Flex alignItems="center" className="p-2 rounded bg-muted" gap={ 1 } justifyContent="between">
|
||||
{ isGuide &&
|
||||
<div className="btn-group">
|
||||
<Button onClick={ visit }>{ LocalizeText('guide.help.request.guide.ongoing.visit.button') }</Button>
|
||||
@ -85,13 +86,13 @@ export const GuideToolOngoingView: FC<GuideToolOngoingViewProps> = props =>
|
||||
</Column> }
|
||||
<Button disabled variant="danger">{ LocalizeText('guide.help.common.report.link') }</Button>
|
||||
</Flex>
|
||||
<Column className="bg-muted rounded chat-messages p-2" gap={ 1 } overflow="hidden">
|
||||
<Column className="p-2 rounded bg-muted chat-messages" gap={ 1 } overflow="hidden">
|
||||
<Column overflow="auto">
|
||||
{ messageGroups.map((group, index) =>
|
||||
{
|
||||
return (
|
||||
<Flex key={ index } fullWidth gap={ 2 } justifyContent={ isOwnChat(group.userId) ? 'end' : 'start' }>
|
||||
<div className="message-avatar flex-shrink-0">
|
||||
<div className="flex-shrink-0 message-avatar">
|
||||
{ (!isOwnChat(group.userId)) &&
|
||||
<LayoutAvatarImageView direction={ 2 } figure={ userFigure } /> }
|
||||
</div>
|
||||
@ -103,7 +104,7 @@ export const GuideToolOngoingView: FC<GuideToolOngoingViewProps> = props =>
|
||||
{ group.messages.map((chat, index) => <div key={ index } className={ classNames(chat.roomId ? 'text-break text-underline' : 'text-break', 'chat.roomId' && 'cursor-pointer') } onClick={ () => chat.roomId ? TryVisitRoom(chat.roomId) : null }>{ chat.message }</div>) }
|
||||
</div>
|
||||
{ (isOwnChat(group.userId)) &&
|
||||
<div className="message-avatar flex-shrink-0">
|
||||
<div className="flex-shrink-0 message-avatar">
|
||||
<LayoutAvatarImageView direction={ 4 } figure={ GetSessionDataManager().figure } />
|
||||
</div> }
|
||||
</Flex>
|
||||
|
@ -140,7 +140,7 @@ export const HcCenterView: FC<{}> = props =>
|
||||
</Button>
|
||||
</Flex>
|
||||
</div>
|
||||
<div className="end-0 p-4 top-0 habbo-avatar position-absolute">
|
||||
<div className="end-0 p-4 top-0 habbo-avatar absolute">
|
||||
<LayoutAvatarImageView direction={ 4 } figure={ userFigure } scale={ 2 } />
|
||||
</div>
|
||||
</Flex>
|
||||
|
@ -90,12 +90,12 @@ export const HotelView: FC<{}> = props =>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="background position-absolute" style={ (background && background.length) ? { backgroundImage: `url(${ background })` } : {} } />
|
||||
<div className="sun position-absolute" style={ (sun && sun.length) ? { backgroundImage: `url(${ sun })` } : {} } />
|
||||
<div className="drape position-absolute" style={ (drape && drape.length) ? { backgroundImage: `url(${ drape })` } : {} } />
|
||||
<div className="left position-absolute" style={ (left && left.length) ? { backgroundImage: `url(${ left })` } : {} } />
|
||||
<div className="right-repeat position-absolute" style={ (rightRepeat && rightRepeat.length) ? { backgroundImage: `url(${ rightRepeat })` } : {} } />
|
||||
<div className="right position-absolute" style={ (right && right.length) ? { backgroundImage: `url(${ right })` } : {} } />
|
||||
<div className="background absolute" style={ (background && background.length) ? { backgroundImage: `url(${ background })` } : {} } />
|
||||
<div className="sun absolute" style={ (sun && sun.length) ? { backgroundImage: `url(${ sun })` } : {} } />
|
||||
<div className="drape absolute" style={ (drape && drape.length) ? { backgroundImage: `url(${ drape })` } : {} } />
|
||||
<div className="left absolute" style={ (left && left.length) ? { backgroundImage: `url(${ left })` } : {} } />
|
||||
<div className="right-repeat absolute" style={ (rightRepeat && rightRepeat.length) ? { backgroundImage: `url(${ rightRepeat })` } : {} } />
|
||||
<div className="right absolute" style={ (right && right.length) ? { backgroundImage: `url(${ right })` } : {} } />
|
||||
{ GetConfigurationValue('hotelview')['show.avatar'] && (
|
||||
<div className="avatar-image">
|
||||
<LayoutAvatarImageView direction={ 2 } figure={ userFigure } />
|
||||
|
@ -33,9 +33,9 @@ export const BonusRareWidgetView: FC<BonusRareWidgetViewProps> = props =>
|
||||
return (
|
||||
<div className="bonus-rare widget flex">
|
||||
{ productType }
|
||||
<div className="bg-light-dark rounded overflow-hidden position-relative bonus-bar-container">
|
||||
<div className="flex justify-center items-center size-full position-absolute small top-0">{ (totalCoinsForBonus - coinsStillRequiredToBuy) + '/' + totalCoinsForBonus }</div>
|
||||
<div className="small bg-info rounded position-absolute top-0 h-100" style={ { width: ((totalCoinsForBonus - coinsStillRequiredToBuy) / totalCoinsForBonus) * 100 + '%' } }></div>
|
||||
<div className="bg-light-dark rounded overflow-hidden relative bonus-bar-container">
|
||||
<div className="flex justify-center items-center size-full absolute small top-0">{ (totalCoinsForBonus - coinsStillRequiredToBuy) + '/' + totalCoinsForBonus }</div>
|
||||
<div className="small bg-info rounded absolute top-0 h-100" style={ { width: ((totalCoinsForBonus - coinsStillRequiredToBuy) / totalCoinsForBonus) * 100 + '%' } }></div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { MouseEventType } from '@nitrots/nitro-renderer';
|
||||
import { FC, MouseEvent, useState } from 'react';
|
||||
import { attemptItemPlacement, GroupItem } from '../../../../api';
|
||||
import { LayoutGridItem } from '../../../../common';
|
||||
import { GroupItem, attemptItemPlacement } from '../../../../api';
|
||||
import { useInventoryFurni } from '../../../../hooks';
|
||||
import { InfiniteGrid, classNames } from '../../../../layout';
|
||||
|
||||
export const InventoryFurnitureItemView: FC<{ groupItem: GroupItem }> = props =>
|
||||
{
|
||||
@ -34,5 +34,5 @@ export const InventoryFurnitureItemView: FC<{ groupItem: GroupItem }> = props =>
|
||||
|
||||
const count = groupItem.getUnlockedCount();
|
||||
|
||||
return <LayoutGridItem className={ !count ? 'opacity-0-5 ' : '' } itemActive={ (groupItem === selectedItem) } itemCount={ groupItem.getUnlockedCount() } itemImage={ groupItem.iconUrl } itemUniqueNumber={ groupItem.stuffData.uniqueNumber } itemUnseen={ groupItem.hasUnseenItems } onDoubleClick={ onMouseEvent } onMouseDown={ onMouseEvent } onMouseOut={ onMouseEvent } onMouseUp={ onMouseEvent } { ...rest } />;
|
||||
return <InfiniteGrid.Item className={ classNames(!count && 'opacity-50') } itemActive={ (groupItem === selectedItem) } itemCount={ groupItem.getUnlockedCount() } itemImage={ groupItem.iconUrl } itemUniqueNumber={ groupItem.stuffData.uniqueNumber } itemUnseen={ groupItem.hasUnseenItems } onDoubleClick={ onMouseEvent } onMouseDown={ onMouseEvent } onMouseOut={ onMouseEvent } onMouseUp={ onMouseEvent } />
|
||||
}
|
||||
|
@ -112,11 +112,11 @@ export const InventoryFurnitureView: FC<InventoryFurnitureViewProps> = props =>
|
||||
if(!groupItems || !groupItems.length) return <InventoryCategoryEmptyView desc={ LocalizeText('inventory.empty.desc') } title={ LocalizeText('inventory.empty.title') } />;
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-12 gap-2">
|
||||
<div className="flex flex-col col-span-7 overflow-hidden">
|
||||
<div className="grid h-full grid-cols-12 gap-2 overflow-hidden">
|
||||
<div className="flex flex-col col-span-7 gap-1 overflow-hidden">
|
||||
<InventoryFurnitureSearchView groupItems={ groupItems } setGroupItems={ setFilteredGroupItems } />
|
||||
<InfiniteGrid<GroupItem>
|
||||
columnCount={ 5 }
|
||||
columnCount={ 6 }
|
||||
itemRender={ item => <InventoryFurnitureItemView groupItem={ item } /> }
|
||||
items={ filteredGroupItems } />
|
||||
</div>
|
||||
|
@ -123,17 +123,17 @@ export const ModToolsView: FC<{}> = props =>
|
||||
<NitroCardView className="nitro-mod-tools" theme="primary-slim" uniqueKey="mod-tools" windowPosition={ DraggableWindowPosition.TOP_LEFT } >
|
||||
<NitroCardHeaderView headerText={ 'Mod Tools' } onCloseClick={ event => setIsVisible(false) } />
|
||||
<NitroCardContentView className="text-black" gap={ 1 }>
|
||||
<Button className="position-relative" disabled={ (currentRoomId <= 0) } gap={ 1 } onClick={ event => CreateLinkEvent(`mod-tools/toggle-room-info/${ currentRoomId }`) }>
|
||||
<div className="icon icon-small-room position-absolute start-1"/> Room Tool
|
||||
<Button className="relative" disabled={ (currentRoomId <= 0) } gap={ 1 } onClick={ event => CreateLinkEvent(`mod-tools/toggle-room-info/${ currentRoomId }`) }>
|
||||
<div className="icon icon-small-room absolute start-1"/> Room Tool
|
||||
</Button>
|
||||
<Button className="position-relative" disabled={ (currentRoomId <= 0) } gap={ 1 } innerRef={ elementRef } onClick={ event => CreateLinkEvent(`mod-tools/toggle-room-chatlog/${ currentRoomId }`) }>
|
||||
<div className="icon icon-chat-history position-absolute start-1"/> Chatlog Tool
|
||||
<Button className="relative" disabled={ (currentRoomId <= 0) } gap={ 1 } innerRef={ elementRef } onClick={ event => CreateLinkEvent(`mod-tools/toggle-room-chatlog/${ currentRoomId }`) }>
|
||||
<div className="icon icon-chat-history absolute start-1"/> Chatlog Tool
|
||||
</Button>
|
||||
<Button className="position-relative" disabled={ !selectedUser } gap={ 1 } onClick={ () => CreateLinkEvent(`mod-tools/toggle-user-info/${ selectedUser.userId }`) }>
|
||||
<div className="icon icon-user position-absolute start-1"/> User: { selectedUser ? selectedUser.username : '' }
|
||||
<Button className="relative" disabled={ !selectedUser } gap={ 1 } onClick={ () => CreateLinkEvent(`mod-tools/toggle-user-info/${ selectedUser.userId }`) }>
|
||||
<div className="icon icon-user absolute start-1"/> User: { selectedUser ? selectedUser.username : '' }
|
||||
</Button>
|
||||
<Button className="position-relative" gap={ 1 } onClick={ () => setIsTicketsVisible(prevValue => !prevValue) }>
|
||||
<div className="icon icon-tickets position-absolute start-1"/> Report Tool
|
||||
<Button className="relative" gap={ 1 } onClick={ () => setIsTicketsVisible(prevValue => !prevValue) }>
|
||||
<div className="icon icon-tickets absolute start-1"/> Report Tool
|
||||
</Button>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView> }
|
||||
|
@ -2,9 +2,10 @@ import { CreateLinkEvent, GetCustomRoomFilterMessageComposer, GetSessionDataMana
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { FaLink } from 'react-icons/fa';
|
||||
import { DispatchUiEvent, GetGroupInformation, LocalizeText, ReportType, SendMessageComposer } from '../../../api';
|
||||
import { Button, Column, Flex, LayoutBadgeImageView, LayoutRoomThumbnailView, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text, UserProfileIconView, classNames } from '../../../common';
|
||||
import { Button, Column, Flex, LayoutBadgeImageView, LayoutRoomThumbnailView, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text, UserProfileIconView } from '../../../common';
|
||||
import { RoomWidgetThumbnailEvent } from '../../../events';
|
||||
import { useHelp, useNavigator } from '../../../hooks';
|
||||
import { classNames } from '../../../layout';
|
||||
|
||||
export class NavigatorRoomInfoViewProps
|
||||
{
|
||||
@ -106,7 +107,7 @@ export const NavigatorRoomInfoView: FC<NavigatorRoomInfoViewProps> = props =>
|
||||
<>
|
||||
<Flex gap={ 2 } overflow="hidden">
|
||||
<LayoutRoomThumbnailView customUrl={ navigatorData.enteredGuestRoom.officialRoomPicRef } roomId={ navigatorData.enteredGuestRoom.roomId }>
|
||||
{ hasPermission('settings') && <i className="icon icon-camera-small position-absolute b-0 r-0 m-1 cursor-pointer top-0" onClick={ () => processAction('open_room_thumbnail_camera') } /> }
|
||||
{ hasPermission('settings') && <i className="top-0 m-1 cursor-pointer icon icon-camera-small absolute b-0 r-0" onClick={ () => processAction('open_room_thumbnail_camera') } /> }
|
||||
</LayoutRoomThumbnailView>
|
||||
<Column grow gap={ 1 } overflow="hidden">
|
||||
<div className="flex gap-1">
|
||||
@ -131,13 +132,13 @@ export const NavigatorRoomInfoView: FC<NavigatorRoomInfoViewProps> = props =>
|
||||
<div className="flex items-center gap-1">
|
||||
{ navigatorData.enteredGuestRoom.tags.map(tag =>
|
||||
{
|
||||
return <Text key={ tag } pointer className="bg-muted rounded p-1" onClick={ event => processAction('navigator_search_tag', tag) }>#{ tag }</Text>
|
||||
return <Text key={ tag } pointer className="p-1 rounded bg-muted" onClick={ event => processAction('navigator_search_tag', tag) }>#{ tag }</Text>
|
||||
}) }
|
||||
</div> }
|
||||
</Column>
|
||||
<Column alignItems="center" gap={ 1 }>
|
||||
{ hasPermission('settings') &&
|
||||
<i className="icon icon-cog cursor-pointer" title={ LocalizeText('navigator.room.popup.info.room.settings') } onClick={ event => processAction('open_room_settings') } /> }
|
||||
<i className="cursor-pointer icon icon-cog" title={ LocalizeText('navigator.room.popup.info.room.settings') } onClick={ event => processAction('open_room_settings') } /> }
|
||||
<FaLink className="cursor-pointer fa-icon" title={ LocalizeText('navigator.embed.caption') } onClick={ event => CreateLinkEvent('navigator/toggle-room-link') } />
|
||||
</Column>
|
||||
</div>
|
||||
|
@ -44,9 +44,9 @@ export const NavigatorSearchResultItemInfoView: FC<{
|
||||
<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={ 'position-absolute top-0 start-0 m-1 ' } isGroup={ true }/>) }
|
||||
<LayoutBadgeImageView badgeCode={ roomData.groupBadgeCode } className={ 'absolute top-0 start-0 m-1 ' } isGroup={ true }/>) }
|
||||
{ roomData.doorMode !== RoomDataParser.OPEN_STATE && (
|
||||
<i className={ 'position-absolute end-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' : '') }/> ) }
|
||||
<i className={ 'absolute end-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="flex-grow-1" style={ { maxHeight: 13 } }>
|
||||
@ -64,7 +64,7 @@ export const NavigatorSearchResultItemInfoView: FC<{
|
||||
<Text className="flex-grow-1">
|
||||
{ roomData.description }
|
||||
</Text>
|
||||
<Flex className={ 'badge p-1 position-absolute m-1 bottom-0 end-0 m-2 ' + getUserCounterColor() } gap={ 1 }>
|
||||
<Flex className={ 'badge p-1 absolute m-1 bottom-0 end-0 m-2 ' + getUserCounterColor() } gap={ 1 }>
|
||||
<FaUser className="fa-icon" />
|
||||
{ roomData.userCount }
|
||||
</Flex>
|
||||
|
@ -83,13 +83,13 @@ 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 }>
|
||||
<LayoutRoomThumbnailView className="flex flex-col items-center justify-content-end mb-1" customUrl={ roomData.officialRoomPicRef } roomId={ roomData.roomId }>
|
||||
{ roomData.habboGroupId > 0 && <LayoutBadgeImageView badgeCode={ roomData.groupBadgeCode } className={ 'position-absolute top-0 start-0 m-1' } isGroup={ true } /> }
|
||||
<Flex center className={ 'badge p-1 position-absolute m-1 ' + getUserCounterColor() } gap={ 1 }>
|
||||
{ roomData.habboGroupId > 0 && <LayoutBadgeImageView badgeCode={ roomData.groupBadgeCode } className={ 'absolute top-0 start-0 m-1' } isGroup={ true } /> }
|
||||
<Flex center className={ 'badge p-1 absolute m-1 ' + getUserCounterColor() } gap={ 1 }>
|
||||
<FaUser className="fa-icon" />
|
||||
{ roomData.userCount }
|
||||
</Flex>
|
||||
{ (roomData.doorMode !== RoomDataParser.OPEN_STATE) &&
|
||||
<i className={ ('position-absolute end-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' : '')) } /> }
|
||||
<i className={ ('absolute end-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>
|
||||
<Flex className="w-100">
|
||||
<Text truncate className="flex-grow-1">{ roomData.roomName }</Text>
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { GetRenderer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect, useRef } from 'react';
|
||||
import { DispatchMouseEvent, DispatchTouchEvent } from '../../api';
|
||||
import { classNames } from '../../common';
|
||||
import { useRoom } from '../../hooks';
|
||||
import { classNames } from '../../layout';
|
||||
import { RoomSpectatorView } from './spectator/RoomSpectatorView';
|
||||
import { RoomWidgetsView } from './widgets/RoomWidgetsView';
|
||||
|
||||
|
@ -154,11 +154,13 @@ export const RoomWidgetsView: FC<{}> = props =>
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="absolute top-0 left-0 pointer-events-none size-full">
|
||||
<FurnitureWidgetsView />
|
||||
</div>
|
||||
<AvatarInfoWidgetView />
|
||||
<ChatWidgetView />
|
||||
<ChatInputView />
|
||||
<DoorbellWidgetView />
|
||||
<FurnitureWidgetsView />
|
||||
<RoomToolsWidgetView />
|
||||
<RoomFilterWordsWidgetView />
|
||||
<RoomThumbnailWidgetView />
|
||||
|
@ -340,11 +340,11 @@ export const InfoStandWidgetFurniView: FC<InfoStandWidgetFurniViewProps> = props
|
||||
<div className="flex flex-col gap-1">
|
||||
<Flex gap={ 1 } position="relative">
|
||||
{ avatarInfo.stuffData.isUnique &&
|
||||
<div className="position-absolute end-0">
|
||||
<div className="absolute end-0">
|
||||
<LayoutLimitedEditionCompactPlateView uniqueNumber={ avatarInfo.stuffData.uniqueNumber } uniqueSeries={ avatarInfo.stuffData.uniqueSeries } />
|
||||
</div> }
|
||||
{ (avatarInfo.stuffData.rarityLevel > -1) &&
|
||||
<div className="position-absolute end-0">
|
||||
<div className="absolute end-0">
|
||||
<LayoutRarityLevelView level={ avatarInfo.stuffData.rarityLevel } />
|
||||
</div> }
|
||||
<Flex center fullWidth>
|
||||
|
@ -98,8 +98,8 @@ export const InfoStandWidgetPetView: FC<InfoStandWidgetPetViewProps> = props =>
|
||||
</Column> }
|
||||
<Column alignItems="center" gap={ 1 }>
|
||||
<Text small truncate variant="white">{ LocalizeText('infostand.pet.text.wellbeing') }</Text>
|
||||
<div className="bg-light-dark rounded position-relative overflow-hidden w-100">
|
||||
<div className="flex justify-center items-center size-full position-absolute">
|
||||
<div className="bg-light-dark rounded relative overflow-hidden w-100">
|
||||
<div className="flex justify-center items-center size-full absolute">
|
||||
<Text small variant="white">{ avatarInfo.dead ? '00:00:00' : ConvertSeconds((remainingTimeToLive == 0 ? avatarInfo.remainingTimeToLive : remainingTimeToLive)).split(':')[1] + ':' + ConvertSeconds((remainingTimeToLive == null || remainingTimeToLive == undefined ? 0 : remainingTimeToLive)).split(':')[2] + ':' + ConvertSeconds((remainingTimeToLive == null || remainingTimeToLive == undefined ? 0 : remainingTimeToLive)).split(':')[3] }</Text>
|
||||
</div>
|
||||
<div className="bg-success rounded pet-stats" style={ { width: avatarInfo.dead ? '0' : Math.round((avatarInfo.maximumTimeToLive * 100) / (remainingTimeToLive)).toString() } } />
|
||||
@ -132,8 +132,8 @@ export const InfoStandWidgetPetView: FC<InfoStandWidgetPetViewProps> = props =>
|
||||
<Text center small wrap variant="white">{ LocalizeText('pet.level', [ 'level', 'maxlevel' ], [ avatarInfo.level.toString(), avatarInfo.maximumLevel.toString() ]) }</Text>
|
||||
<Column alignItems="center" gap={ 1 }>
|
||||
<Text small truncate variant="white">{ LocalizeText('infostand.pet.text.happiness') }</Text>
|
||||
<div className="bg-light-dark rounded position-relative overflow-hidden w-100">
|
||||
<div className="flex justify-center items-center size-full position-absolute">
|
||||
<div className="bg-light-dark rounded relative overflow-hidden w-100">
|
||||
<div className="flex justify-center items-center size-full absolute">
|
||||
<Text small variant="white">{ avatarInfo.happyness + '/' + avatarInfo.maximumHappyness }</Text>
|
||||
</div>
|
||||
<div className="bg-info rounded pet-stats" style={ { width: (avatarInfo.happyness / avatarInfo.maximumHappyness) * 100 + '%' } } />
|
||||
@ -141,8 +141,8 @@ export const InfoStandWidgetPetView: FC<InfoStandWidgetPetViewProps> = props =>
|
||||
</Column>
|
||||
<Column alignItems="center" gap={ 1 }>
|
||||
<Text small truncate variant="white">{ LocalizeText('infostand.pet.text.experience') }</Text>
|
||||
<div className="bg-light-dark rounded position-relative overflow-hidden w-100">
|
||||
<div className="flex justify-center items-center size-full position-absolute">
|
||||
<div className="bg-light-dark rounded relative overflow-hidden w-100">
|
||||
<div className="flex justify-center items-center size-full absolute">
|
||||
<Text small variant="white">{ avatarInfo.experience + '/' + avatarInfo.levelExperienceGoal }</Text>
|
||||
</div>
|
||||
<div className="bg-purple rounded pet-stats" style={ { width: (avatarInfo.experience / avatarInfo.levelExperienceGoal) * 100 + '%' } } />
|
||||
@ -150,8 +150,8 @@ export const InfoStandWidgetPetView: FC<InfoStandWidgetPetViewProps> = props =>
|
||||
</Column>
|
||||
<Column alignItems="center" gap={ 1 }>
|
||||
<Text small truncate variant="white">{ LocalizeText('infostand.pet.text.energy') }</Text>
|
||||
<div className="bg-light-dark rounded position-relative overflow-hidden w-100">
|
||||
<div className="flex justify-center items-center size-full position-absolute">
|
||||
<div className="bg-light-dark rounded relative overflow-hidden w-100">
|
||||
<div className="flex justify-center items-center size-full absolute">
|
||||
<Text small variant="white">{ avatarInfo.energy + '/' + avatarInfo.maximumEnergy }</Text>
|
||||
</div>
|
||||
<div className="bg-success rounded pet-stats" style={ { width: (avatarInfo.energy / avatarInfo.maximumEnergy) * 100 + '%' } } />
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { GetSessionDataManager } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect, useMemo, useState } from 'react';
|
||||
import { LocalizeText, RoomObjectItem } from '../../../../api';
|
||||
import { Flex, InfiniteScroll, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text, classNames } from '../../../../common';
|
||||
import { Flex, InfiniteScroll, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common';
|
||||
import { classNames } from '../../../../layout';
|
||||
|
||||
interface ChooserWidgetViewProps
|
||||
{
|
||||
|
@ -94,7 +94,7 @@ export const ContextMenuView: FC<ContextMenuViewProps> = props =>
|
||||
|
||||
const getClassNames = useMemo(() =>
|
||||
{
|
||||
const newClassNames: string[] = [ 'nitro-context-menu', 'position-absolute' ];
|
||||
const newClassNames: string[] = [ 'nitro-context-menu', 'absolute' ];
|
||||
|
||||
if (isCollapsed) newClassNames.push('menu-hidden');
|
||||
|
||||
|
@ -2,8 +2,9 @@ import { RoomEngineTriggerWidgetEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect, useMemo, useState } from 'react';
|
||||
import ReactSlider from 'react-slider';
|
||||
import { ColorUtils, FurnitureDimmerUtilities, GetConfigurationValue, LocalizeText } from '../../../../api';
|
||||
import { Button, Column, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView, Text, classNames } from '../../../../common';
|
||||
import { Button, Column, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView, Text } from '../../../../common';
|
||||
import { useFurnitureDimmerWidget, useNitroEvent } from '../../../../hooks';
|
||||
import { classNames } from '../../../../layout';
|
||||
|
||||
export const FurnitureDimmerView: FC<{}> = props =>
|
||||
{
|
||||
@ -41,7 +42,7 @@ export const FurnitureDimmerView: FC<{}> = props =>
|
||||
{ (dimmerState === 0) &&
|
||||
<Column alignItems="center">
|
||||
<div className="dimmer-banner" />
|
||||
<Text center className="bg-muted rounded p-1">{ LocalizeText('widget.dimmer.info.off') }</Text>
|
||||
<Text center className="p-1 rounded bg-muted">{ LocalizeText('widget.dimmer.info.off') }</Text>
|
||||
<Button fullWidth variant="success" onClick={ () => FurnitureDimmerUtilities.changeState() }>{ LocalizeText('widget.dimmer.button.on') }</Button>
|
||||
</Column> }
|
||||
{ (dimmerState === 1) &&
|
||||
|
@ -86,10 +86,10 @@ export const FurnitureMannequinView: FC<{}> = props =>
|
||||
<NitroCardContentView center>
|
||||
<div className="flex w-100 gap-2 overflow-hidden">
|
||||
<div className="flex flex-col">
|
||||
<div className="position-relative mannequin-preview">
|
||||
<div className="relative mannequin-preview">
|
||||
<LayoutAvatarImageView direction={ 2 } figure={ renderedFigure } position="absolute" />
|
||||
{ (clubLevel > 0) &&
|
||||
<LayoutCurrencyIcon className="position-absolute end-2 bottom-2" type="hc" /> }
|
||||
<LayoutCurrencyIcon className="absolute end-2 bottom-2" type="hc" /> }
|
||||
</div>
|
||||
</div>
|
||||
<Column grow justifyContent="between" overflow="auto">
|
||||
|
@ -22,7 +22,7 @@ import { FurniturePlaylistEditorWidgetView } from './playlist-editor/FurniturePl
|
||||
export const FurnitureWidgetsView: FC<{}> = props =>
|
||||
{
|
||||
return (
|
||||
<div className="position-absolute size-full nitro-room-widgets top-0 start-0">
|
||||
<>
|
||||
<FurnitureBackgroundColorView />
|
||||
<FurnitureBadgeDisplayView />
|
||||
<FurnitureCraftingView />
|
||||
@ -42,6 +42,6 @@ export const FurnitureWidgetsView: FC<{}> = props =>
|
||||
<FurnitureTrophyView />
|
||||
<FurnitureContextMenuView />
|
||||
<FurnitureYoutubeDisplayView />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -16,10 +16,10 @@ export const FurniturePlaylistEditorWidgetView: FC<{}> = props =>
|
||||
<NitroCardHeaderView headerText={ LocalizeText('playlist.editor.title') } onCloseClick={ onClose } />
|
||||
<NitroCardContentView>
|
||||
<div className="flex flex-row gap-1 h-100">
|
||||
<div className="w-50 position-relative overflow-hidden h-100 rounded flex flex-col">
|
||||
<div className="w-50 relative overflow-hidden h-100 rounded flex flex-col">
|
||||
<DiskInventoryView addToPlaylist={ addToPlaylist } diskInventory={ diskInventory } />
|
||||
</div>
|
||||
<div className="w-50 position-relative overflow-hidden h-100 rounded flex flex-col">
|
||||
<div className="w-50 relative overflow-hidden h-100 rounded flex flex-col">
|
||||
<SongPlaylistView currentPlayingIndex={ currentPlayingIndex } furniId={ objectId } playlist={ playlist } removeFromPlaylist={ removeFromPlaylist } togglePlayPause={ togglePlayPause }/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -57,5 +57,5 @@ export const ObjectLocationView: FC<ObjectLocationViewProps> = props =>
|
||||
}
|
||||
}, [ objectId, category, noFollow ]);
|
||||
|
||||
return <div ref={ elementRef } className="object-location position-absolute" style={ { left: pos.x, top: pos.y, visibility: ((pos.x + (elementRef.current ? elementRef.current.offsetWidth : 0)) > -1) ? 'visible' : 'hidden' } } { ...rest } />;
|
||||
return <div ref={ elementRef } className="object-location absolute" style={ { left: pos.x, top: pos.y, visibility: ((pos.x + (elementRef.current ? elementRef.current.offsetWidth : 0)) > -1) ? 'visible' : 'hidden' } } { ...rest } />;
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { UpdateRoomFilterMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useState } from 'react';
|
||||
import { LocalizeText, SendMessageComposer } from '../../../../api';
|
||||
import { Button, Column, Flex, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text, classNames } from '../../../../common';
|
||||
import { Button, Column, Flex, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../common';
|
||||
import { useFilterWordsWidget, useNavigator } from '../../../../hooks';
|
||||
import { classNames } from '../../../../layout';
|
||||
|
||||
export const RoomFilterWordsWidgetView: FC<{}> = props =>
|
||||
{
|
||||
@ -51,7 +52,7 @@ export const RoomFilterWordsWidgetView: FC<{}> = props =>
|
||||
<NitroCardView className="nitro-guide-tool no-resize" theme="primary-slim">
|
||||
<NitroCardHeaderView headerText={ LocalizeText('navigator.roomsettings.roomfilter') } onCloseClick={ () => onClose() } />
|
||||
<NitroCardContentView className="text-black">
|
||||
<Grid className="flex items-center justify-content-end gap-2">
|
||||
<Grid className="flex items-center gap-2 justify-content-end">
|
||||
<input className="form-control form-control-sm" maxLength={ 255 } type="text" value={ word } onChange={ event => onTyping(event.target.value) } />
|
||||
<Button onClick={ () => processAction(true) }>{ LocalizeText('navigator.roomsettings.roomfilter.addword') }</Button>
|
||||
</Grid>
|
||||
@ -65,7 +66,7 @@ export const RoomFilterWordsWidgetView: FC<{}> = props =>
|
||||
)
|
||||
}) }
|
||||
</Column>
|
||||
<Grid className="flex items-center justify-content-end gap-2">
|
||||
<Grid className="flex items-center gap-2 justify-content-end">
|
||||
<Button disabled={ wordsFilter.length === 0 || !isSelectingWord } variant="danger" onClick={ () => processAction(false) }>{ LocalizeText('navigator.roomsettings.roomfilter.removeword') }</Button>
|
||||
</Grid>
|
||||
</NitroCardContentView>
|
||||
|
@ -18,8 +18,8 @@ export const RoomPromoteOtherEventWidgetView: FC<RoomPromoteOtherEventWidgetView
|
||||
</Flex>
|
||||
<br /><br />
|
||||
<Column alignItems="center" gap={ 1 }>
|
||||
<div className="bg-light-dark rounded position-relative overflow-hidden w-100">
|
||||
<div className="flex justify-center items-center size-full position-absolute">
|
||||
<div className="bg-light-dark rounded relative overflow-hidden w-100">
|
||||
<div className="flex justify-center items-center size-full absolute">
|
||||
<Text center variant="white">{ LocalizeText('navigator.eventinprogress') }</Text>
|
||||
</div>
|
||||
<Text> </Text>
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { CreateLinkEvent, GetGuestRoomResultEvent, GetRoomEngine, NavigatorSearchComposer, RateFlatMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { LocalizeText, SendMessageComposer } from '../../../../api';
|
||||
import { Text, TransitionAnimation, TransitionAnimationTypes, classNames } from '../../../../common';
|
||||
import { Text, TransitionAnimation, TransitionAnimationTypes } from '../../../../common';
|
||||
import { useMessageEvent, useNavigator, useRoom } from '../../../../hooks';
|
||||
import { classNames } from '../../../../layout';
|
||||
|
||||
export const RoomToolsWidgetView: FC<{}> = props =>
|
||||
{
|
||||
@ -72,24 +73,24 @@ export const RoomToolsWidgetView: FC<{}> = props =>
|
||||
|
||||
return (
|
||||
<div className="flex gap-2 nitro-room-tools-container">
|
||||
<div className="flex flex-col justify-center items-center nitro-room-tools p-2">
|
||||
<div className="icon icon-cog cursor-pointer" title={ LocalizeText('room.settings.button.text') } onClick={ () => handleToolClick('settings') } />
|
||||
<div className="flex flex-col items-center justify-center p-2 nitro-room-tools">
|
||||
<div className="cursor-pointer icon icon-cog" title={ LocalizeText('room.settings.button.text') } onClick={ () => handleToolClick('settings') } />
|
||||
<div className={ classNames('cursor-pointer', 'icon', (!isZoomedIn && 'icon-zoom-less'), (isZoomedIn && 'icon-zoom-more')) } title={ LocalizeText('room.zoom.button.text') } onClick={ () => handleToolClick('zoom') } />
|
||||
<div className="icon icon-chat-history cursor-pointer" title={ LocalizeText('room.chathistory.button.text') } onClick={ () => handleToolClick('chat_history') } />
|
||||
<div className="cursor-pointer icon icon-chat-history" title={ LocalizeText('room.chathistory.button.text') } onClick={ () => handleToolClick('chat_history') } />
|
||||
{ navigatorData.canRate &&
|
||||
<div className="icon icon-like-room cursor-pointer" title={ LocalizeText('room.like.button.text') } onClick={ () => handleToolClick('like_room') } /> }
|
||||
<div className="cursor-pointer icon icon-like-room" title={ LocalizeText('room.like.button.text') } onClick={ () => handleToolClick('like_room') } /> }
|
||||
</div>
|
||||
<div className="flex flex-col justify-center">
|
||||
<TransitionAnimation inProp={ isOpen } timeout={ 300 } type={ TransitionAnimationTypes.SLIDE_LEFT }>
|
||||
<div className="flex flex-col justify-center items-center">
|
||||
<div className="flex flex-col nitro-room-tools-info rounded py-2 px-3">
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
<div className="flex flex-col px-3 py-2 rounded nitro-room-tools-info">
|
||||
<div className="flex flex-col gap-1">
|
||||
<Text wrap fontSize={ 4 } variant="white">{ roomName }</Text>
|
||||
<Text fontSize={ 5 } variant="muted">{ roomOwner }</Text>
|
||||
</div>
|
||||
{ roomTags && roomTags.length > 0 &&
|
||||
<div className="flex gap-2">
|
||||
{ roomTags.map((tag, index) => <Text key={ index } pointer small className="rounded bg-primary p-1" variant="white" onClick={ () => handleToolClick('navigator_search_tag', tag) }>#{ tag }</Text>) }
|
||||
{ roomTags.map((tag, index) => <Text key={ index } pointer small className="p-1 rounded bg-primary" variant="white" onClick={ () => handleToolClick('navigator_search_tag', tag) }>#{ tag }</Text>) }
|
||||
</div> }
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { DetailedHTMLProps, forwardRef, HTMLAttributes, PropsWithChildren } from 'react';
|
||||
import { classNames } from '../../common';
|
||||
import { classNames } from '../../layout';
|
||||
|
||||
export const ToolbarItemView = forwardRef<HTMLDivElement, PropsWithChildren<{
|
||||
icon: string;
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { CreateLinkEvent, Dispose, DropBounce, EaseOut, GetSessionDataManager, JumpBy, Motions, NitroToolbarAnimateIconEvent, PerkAllowancesMessageEvent, PerkEnum, Queue, Wait } from '@nitrots/nitro-renderer';
|
||||
import { FC, useState } from 'react';
|
||||
import { GetConfigurationValue, MessengerIconState, OpenMessengerChat, VisitDesktop } from '../../api';
|
||||
import { LayoutAvatarImageView, LayoutItemCountView, TransitionAnimation, TransitionAnimationTypes, classNames } from '../../common';
|
||||
import { LayoutAvatarImageView, LayoutItemCountView, TransitionAnimation, TransitionAnimationTypes } from '../../common';
|
||||
import { useAchievements, useFriends, useInventoryUnseenTracker, useMessageEvent, useMessenger, useNitroEvent, useSessionInfo } from '../../hooks';
|
||||
import { classNames } from '../../layout';
|
||||
import { ToolbarItemView } from './ToolbarItemView';
|
||||
import { ToolbarMeView } from './ToolbarMeView';
|
||||
|
||||
|
@ -74,7 +74,7 @@ export const GroupsContainerView: FC<GroupsContainerViewProps> = props =>
|
||||
return (
|
||||
<LayoutGridItem key={ index } className="p-1" itemActive={ (selectedGroupId === group.groupId) } overflow="unset" onClick={ () => setSelectedGroupId(group.groupId) }>
|
||||
{ itsMe &&
|
||||
<i className={ 'position-absolute end-0 top-0 z-index-1 icon icon-group-' + (group.favourite ? 'favorite' : 'not-favorite') } onClick={ () => ToggleFavoriteGroup(group) } /> }
|
||||
<i className={ 'absolute end-0 top-0 z-index-1 icon icon-group-' + (group.favourite ? 'favorite' : 'not-favorite') } onClick={ () => ToggleFavoriteGroup(group) } /> }
|
||||
<LayoutBadgeImageView badgeCode={ group.badgeCode } isGroup={ true } />
|
||||
</LayoutGridItem>
|
||||
)
|
||||
|
@ -2,8 +2,9 @@ import { AddLinkEventTracker, ILinkEventTracker, NitroSettingsEvent, RemoveLinkE
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { FaVolumeDown, FaVolumeMute, FaVolumeUp } from 'react-icons/fa';
|
||||
import { DispatchMainEvent, DispatchUiEvent, LocalizeText, SendMessageComposer } from '../../api';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView, Text, classNames } from '../../common';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../common';
|
||||
import { useCatalogPlaceMultipleItems, useCatalogSkipPurchaseConfirmation, useMessageEvent } from '../../hooks';
|
||||
import { classNames } from '../../layout';
|
||||
|
||||
export const UserSettingsView: FC<{}> = props =>
|
||||
{
|
||||
|
@ -22,12 +22,20 @@ body {
|
||||
width: 0.25rem;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
@apply rounded-md;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
@apply rounded-md;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar:horizontal {
|
||||
height: 0.25rem;
|
||||
@apply h-1;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar:not(:horizontal) {
|
||||
width: 0.25rem;
|
||||
@apply h-1;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track:horizontal {
|
||||
@ -690,5 +698,14 @@ body {
|
||||
width: 18px;
|
||||
height: 19px;
|
||||
}
|
||||
|
||||
&.icon-loading {
|
||||
background-image: url("@/assets/images/ui/loading_icon.png");
|
||||
|
||||
&.with-size {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||
import { Fragment, ReactElement, useEffect, useRef } from 'react';
|
||||
import { DetailedHTMLProps, Fragment, HTMLAttributes, ReactElement, forwardRef, useEffect, useRef, useState } from 'react';
|
||||
import { classNames } from './classNames';
|
||||
import { styleNames } from './styleNames';
|
||||
|
||||
type Props<T> = {
|
||||
items: T[];
|
||||
@ -9,7 +11,7 @@ type Props<T> = {
|
||||
itemRender?: (item: T, index?: number) => ReactElement;
|
||||
}
|
||||
|
||||
export const InfiniteGrid = <T,>(props: Props<T>) =>
|
||||
const InfiniteGridRoot = <T,>(props: Props<T>) =>
|
||||
{
|
||||
const { items = [], columnCount = 4, overscan = 5, estimateSize = 45, itemRender = null } = props;
|
||||
const parentRef = useRef<HTMLDivElement>(null);
|
||||
@ -21,6 +23,29 @@ export const InfiniteGrid = <T,>(props: Props<T>) =>
|
||||
estimateSize: () => estimateSize
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
const element = parentRef.current;
|
||||
|
||||
if(!element || !items) return;
|
||||
|
||||
const checkAndApplyPadding = () =>
|
||||
{
|
||||
if(!element) return;
|
||||
|
||||
element.style.paddingRight = (element.scrollHeight > element.clientHeight) ? '0.25rem' : '0';
|
||||
}
|
||||
|
||||
checkAndApplyPadding();
|
||||
|
||||
window.addEventListener('resize', checkAndApplyPadding);
|
||||
|
||||
return () =>
|
||||
{
|
||||
window.removeEventListener('resize', checkAndApplyPadding);
|
||||
}
|
||||
}, [ items ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!items || !items.length) return;
|
||||
@ -31,23 +56,22 @@ export const InfiniteGrid = <T,>(props: Props<T>) =>
|
||||
const virtualItems = virtualizer.getVirtualItems();
|
||||
|
||||
return (
|
||||
<div ref={ parentRef } className="size-full position-relative" style={ { overflowY: 'auto', height: virtualizer.getTotalSize() } }>
|
||||
<div
|
||||
ref={ parentRef }
|
||||
className="overflow-y-auto size-full">
|
||||
<div
|
||||
className="flex flex-col w-full gap-1"
|
||||
className="flex flex-col w-full *:pb-1 relative"
|
||||
style={ {
|
||||
transform: `translateY(${ virtualItems[0]?.start ?? 0 }px)`
|
||||
height: virtualizer.getTotalSize()
|
||||
} }>
|
||||
{ virtualItems.map(virtualRow => (
|
||||
<div
|
||||
key={ virtualRow.key + 'a' }
|
||||
ref={ virtualizer.measureElement }
|
||||
className="grid grid-cols-12 gap-2 "
|
||||
className={ `grid grid-cols-${ columnCount } gap-1 absolute top-0 left-0 h-[45px] last:pb-0 w-full` }
|
||||
data-index={ virtualRow.index }
|
||||
style={ {
|
||||
display: 'grid',
|
||||
gap: '0.25rem',
|
||||
minHeight: virtualRow.index === 0 ? estimateSize : virtualRow.size,
|
||||
gridTemplateColumns: `repeat(${ columnCount }, 1fr)`
|
||||
transform: `translateY(${ virtualRow.start }px)`
|
||||
} }>
|
||||
{ Array.from(Array(columnCount)).map((e,i) =>
|
||||
{
|
||||
@ -68,3 +92,73 @@ export const InfiniteGrid = <T,>(props: Props<T>) =>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const InfiniteGridItem = forwardRef<HTMLDivElement, {
|
||||
itemImage?: string;
|
||||
itemColor?: string;
|
||||
itemActive?: boolean;
|
||||
itemCount?: number;
|
||||
itemCountMinimum?: number;
|
||||
itemUniqueSoldout?: boolean;
|
||||
itemUniqueNumber?: number;
|
||||
itemUnseen?: boolean;
|
||||
itemHighlight?: boolean;
|
||||
disabled?: boolean;
|
||||
} & DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>>((props, ref) =>
|
||||
{
|
||||
const { itemImage = undefined, itemColor = undefined, itemActive = false, itemCount = 1, itemCountMinimum = 1, itemUniqueSoldout = false, itemUniqueNumber = -2, itemUnseen = false, itemHighlight = false, disabled = false, className = null, style = {}, children = null, ...rest } = props;
|
||||
const [ backgroundImageUrl, setBackgroundImageUrl ] = useState<string>(null);
|
||||
const disposed = useRef<boolean>(false);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!itemImage || !itemImage.length) return;
|
||||
|
||||
const image = new Image();
|
||||
|
||||
image.onload = () =>
|
||||
{
|
||||
if(disposed.current) return;
|
||||
|
||||
setBackgroundImageUrl(image.src);
|
||||
}
|
||||
|
||||
image.src = itemImage;
|
||||
}, [ itemImage ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
disposed.current = false;
|
||||
|
||||
return () =>
|
||||
{
|
||||
disposed.current = true;
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ ref }
|
||||
className={ classNames(
|
||||
'flex flex-col items-center justify-center cursor-pointer overflow-hidden relative bg-center bg-no-repeat w-full rounded-md border-2',
|
||||
(!backgroundImageUrl || !backgroundImageUrl.length) && 'nitro-icon icon-loading',
|
||||
itemActive ? 'border-card-grid-item-active bg-card-grid-item-active' : 'border-card-grid-item-border bg-card-grid-item',
|
||||
className
|
||||
) }
|
||||
style={ styleNames(
|
||||
backgroundImageUrl && backgroundImageUrl.length && !(itemUniqueSoldout || (itemUniqueNumber > 0)) && {
|
||||
backgroundImage: `url(${ backgroundImageUrl })`
|
||||
},
|
||||
style
|
||||
) }
|
||||
{ ...rest }>
|
||||
{ children }
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
InfiniteGridItem.displayName = 'InfiniteGridItem';
|
||||
|
||||
export const InfiniteGrid = Object.assign(InfiniteGridRoot, {
|
||||
Item: InfiniteGridItem
|
||||
});
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { DetailedHTMLProps, forwardRef, HTMLAttributes, MouseEvent, PropsWithChildren } from 'react';
|
||||
import { classNames, DraggableWindow, DraggableWindowPosition, DraggableWindowProps } from '../common';
|
||||
import { DraggableWindow, DraggableWindowPosition, DraggableWindowProps } from '../common';
|
||||
import { classNames } from './classNames';
|
||||
import { NitroItemCountBadge } from './NitroItemCountBadge';
|
||||
|
||||
const NitroCardRoot = forwardRef<HTMLDivElement, PropsWithChildren<{
|
||||
@ -57,7 +58,7 @@ const NitroCardContent = forwardRef<HTMLDivElement, {
|
||||
<div
|
||||
ref={ ref }
|
||||
className={ classNames(
|
||||
'overflow-auto bg-card-content-area p-2 h-full',
|
||||
'flex flex-col overflow-auto bg-card-content-area p-2 h-full',
|
||||
className
|
||||
) }
|
||||
{ ...rest }>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { DetailedHTMLProps, forwardRef, HTMLAttributes, PropsWithChildren } from 'react';
|
||||
import { classNames } from '../common';
|
||||
import { classNames } from './classNames';
|
||||
|
||||
const classes = {
|
||||
base: 'top-2 right-2 py-0.5 px-[3px] z-[1] rounded border',
|
||||
|
@ -1,3 +1,5 @@
|
||||
export * from './InfiniteGrid';
|
||||
export * from './NitroCard';
|
||||
export * from './NitroItemCountBadge';
|
||||
export * from './classNames';
|
||||
export * from './styleNames';
|
||||
|
8
src/layout/styleNames.ts
Normal file
8
src/layout/styleNames.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export const styleNames = (...styles: object[]) =>
|
||||
{
|
||||
let mergedStyle = {};
|
||||
|
||||
styles.filter(Boolean).forEach(style => mergedStyle = { ...mergedStyle, ...style });
|
||||
|
||||
return mergedStyle;
|
||||
}
|
@ -10,7 +10,11 @@ const colors = {
|
||||
'card-border': '#283F5D',
|
||||
'card-tab-item': '#B6BEC5',
|
||||
'card-tab-item-active': '#DFDFDF',
|
||||
'card-content-area': '#DFDFDF'
|
||||
'card-content-area': '#DFDFDF',
|
||||
'card-grid-item': '#CDD3D9',
|
||||
'card-grid-item-active': '#ECECEC',
|
||||
'card-grid-item-border': '#B6BEC5',
|
||||
'card-grid-item-border-active': '#FFFFFF',
|
||||
};
|
||||
|
||||
const boxShadow = {
|
||||
@ -39,6 +43,20 @@ module.exports = {
|
||||
}
|
||||
},
|
||||
},
|
||||
safelist: [
|
||||
'grid-cols-1',
|
||||
'grid-cols-2',
|
||||
'grid-cols-3',
|
||||
'grid-cols-4',
|
||||
'grid-cols-5',
|
||||
'grid-cols-6',
|
||||
'grid-cols-7',
|
||||
'grid-cols-8',
|
||||
'grid-cols-9',
|
||||
'grid-cols-10',
|
||||
'grid-cols-11',
|
||||
'grid-cols-12'
|
||||
],
|
||||
darkMode: 'class',
|
||||
plugins: [
|
||||
require('@tailwindcss/forms'),
|
||||
|
Loading…
Reference in New Issue
Block a user