mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-26 07:30:51 +01:00
Start big update
This commit is contained in:
parent
21409b77e0
commit
e4fbb47853
6
.editorconfig
Normal file
6
.editorconfig
Normal file
@ -0,0 +1,6 @@
|
||||
[*]
|
||||
charset = utf-8
|
||||
insert_final_newline = true
|
||||
end_of_line = lf
|
||||
indent_style = space
|
||||
indent_size = 4
|
@ -105,6 +105,17 @@
|
||||
{
|
||||
"prevent": true
|
||||
}
|
||||
],
|
||||
"react/jsx-sort-props": [
|
||||
"error",
|
||||
{
|
||||
"callbacksLast": true,
|
||||
"shorthandFirst": true,
|
||||
"shorthandLast": false,
|
||||
"ignoreCase": true,
|
||||
"noSortAlphabetically": false,
|
||||
"reservedFirst": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
17
.vscode/settings.json
vendored
17
.vscode/settings.json
vendored
@ -8,9 +8,9 @@
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.fixAll.sortJSON": "never",
|
||||
"source.organizeImports": "explicit"
|
||||
"source.organizeImports": "always"
|
||||
},
|
||||
"editor.formatOnSave": false,
|
||||
"editor.formatOnSave": true,
|
||||
"git.ignoreLimitWarning": true,
|
||||
"files.eol": "\n",
|
||||
"files.insertFinalNewline": true,
|
||||
@ -27,6 +27,15 @@
|
||||
}
|
||||
],
|
||||
"javascript.format.enable": false,
|
||||
"thunder-client.saveToWorkspace": false,
|
||||
"thunder-client.workspaceRelativePath": "."
|
||||
"editor.linkedEditing": false,
|
||||
"tailwindCSS.includeLanguages": {
|
||||
"html": "html",
|
||||
"javascript": "javascript",
|
||||
"css": "css"
|
||||
},
|
||||
"tailwindCSS.experimental.classRegex": [
|
||||
],
|
||||
"editor.quickSuggestions": {
|
||||
"strings": true
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root" class="w-100 h-100"></div>
|
||||
<div id="root" class="w-full h-full"></div>
|
||||
<script>
|
||||
window.NitroConfig = {
|
||||
"config.urls": [ '/renderer-config.json', '/ui-config.json' ],
|
||||
|
10
package.json
10
package.json
@ -10,7 +10,10 @@
|
||||
"eslint": "eslint src --ext .ts,.tsx"
|
||||
},
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^1.7.18",
|
||||
"@headlessui/tailwindcss": "^0.2.0",
|
||||
"@tanstack/react-virtual": "3.2.0",
|
||||
"dompurify": "^3.1.0",
|
||||
"react": "^18.2.0",
|
||||
"react-bootstrap": "^2.2.2",
|
||||
"react-dom": "^18.2.0",
|
||||
@ -20,6 +23,7 @@
|
||||
"use-between": "^1.3.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/forms": "^0.5.7",
|
||||
"@types/node": "^20.11.30",
|
||||
"@types/react": "^18.2.67",
|
||||
"@types/react-dom": "^18.2.22",
|
||||
@ -27,12 +31,18 @@
|
||||
"@typescript-eslint/eslint-plugin": "^7.3.1",
|
||||
"@typescript-eslint/parser": "^7.3.1",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.8.0",
|
||||
"eslint-plugin-react": "^7.34.1",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"postcss": "^8.4.38",
|
||||
"postcss-nested": "^6.0.1",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier-plugin-tailwindcss": "^0.5.13",
|
||||
"sass": "^1.72.0",
|
||||
"tailwindcss": "^3.4.3",
|
||||
"typescript": "^5.4.2",
|
||||
"vite": "^5.1.6"
|
||||
}
|
||||
|
8
postcss.config.js
Normal file
8
postcss.config.js
Normal file
@ -0,0 +1,8 @@
|
||||
/** @type {import("postcss-load-config").Config} */
|
||||
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {}
|
||||
}
|
||||
}
|
7
prettier.config.js
Normal file
7
prettier.config.js
Normal file
@ -0,0 +1,7 @@
|
||||
/** @type {import("prettier").Config} */
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
'prettier-plugin-tailwindcss'
|
||||
]
|
||||
}
|
18
src/App.tsx
18
src/App.tsx
@ -1,9 +1,9 @@
|
||||
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 { Base } from './common';
|
||||
import { classNames } from './common';
|
||||
import { MainView } from './components/MainView';
|
||||
import { LoadingView } from './components/loading/LoadingView';
|
||||
import { MainView } from './components/main/MainView';
|
||||
import { useMessageEvent } from './hooks';
|
||||
|
||||
NitroVersion.UI_VERSION = GetUIVersion();
|
||||
@ -34,7 +34,9 @@ export const App: FC<{}> = props =>
|
||||
height,
|
||||
autoDensity: true,
|
||||
backgroundAlpha: 0,
|
||||
preference: 'webgl'
|
||||
preference: 'webgl',
|
||||
eventMode: 'none',
|
||||
failIfMajorPerformanceCaveat: false
|
||||
});
|
||||
|
||||
await GetConfiguration().init();
|
||||
@ -56,12 +58,12 @@ export const App: FC<{}> = props =>
|
||||
GetSoundManager().init(),
|
||||
GetSessionDataManager().init(),
|
||||
GetRoomSessionManager().init(),
|
||||
GetRoomCameraWidgetManager().init()
|
||||
GetRoomCameraWidgetManager().init(),
|
||||
GetCommunication().init()
|
||||
]
|
||||
);
|
||||
|
||||
await GetRoomEngine().init();
|
||||
await GetCommunication().init();
|
||||
|
||||
// new GameMessageHandler();
|
||||
|
||||
@ -91,11 +93,11 @@ export const App: FC<{}> = props =>
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Base fit overflow="hidden" className={ !(window.devicePixelRatio % 1) && 'image-rendering-pixelated' }>
|
||||
<div className={ classNames('w-full h-full overflow-hidden', !(window.devicePixelRatio % 1) && '[image-rendering:pixelated]' ) }>
|
||||
{ !isReady &&
|
||||
<LoadingView /> }
|
||||
{ isReady && <MainView /> }
|
||||
<Base id="draggable-windows-container" />
|
||||
</Base>
|
||||
<div id="draggable-windows-container" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -82,7 +82,11 @@ export class AvatarEditorThumbnailsHelper
|
||||
}
|
||||
}
|
||||
|
||||
if(!hasAsset) continue;
|
||||
if(!hasAsset)
|
||||
{
|
||||
console.log(`${ AvatarFigurePartType.SCALE }_${ AvatarFigurePartType.STD }_${ part.type }_${ part.id }`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const x = asset.offsetX;
|
||||
const y = asset.offsetY;
|
||||
|
@ -1,22 +0,0 @@
|
||||
import { FC, useMemo } from 'react';
|
||||
import { Base, BaseProps } from './Base';
|
||||
|
||||
export interface ButtonGroupProps extends BaseProps<HTMLDivElement>
|
||||
{
|
||||
}
|
||||
|
||||
export const ButtonGroup: FC<ButtonGroupProps> = props =>
|
||||
{
|
||||
const { classNames = [], ...rest } = props;
|
||||
|
||||
const getClassNames = useMemo(() =>
|
||||
{
|
||||
const newClassNames: string[] = [ 'btn-group' ];
|
||||
|
||||
if(classNames.length) newClassNames.push(...classNames);
|
||||
|
||||
return newClassNames;
|
||||
}, [ classNames ]);
|
||||
|
||||
return <Base classNames={ getClassNames } { ...rest } />;
|
||||
}
|
@ -39,12 +39,12 @@ export const Flex: FC<FlexProps> = props =>
|
||||
|
||||
if(justifyContent) newClassNames.push('justify-content-' + justifyContent);
|
||||
|
||||
if(!alignItems && !justifyContent && center) newClassNames.push('align-items-center', 'justify-content-center');
|
||||
if(!alignItems && !justifyContent && center) newClassNames.push('items-center', 'justify-center');
|
||||
|
||||
if(classNames.length) newClassNames.push(...classNames);
|
||||
|
||||
return newClassNames;
|
||||
}, [ column, reverse, gap, center, alignSelf, alignItems, justifyContent, classNames ]);
|
||||
|
||||
return <Base display={ display } classNames={ getClassNames } { ...rest } />;
|
||||
return <Base classNames={ getClassNames } display={ display } { ...rest } />;
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ export const Grid: FC<GridProps> = props =>
|
||||
|
||||
if(justifyContent) newClassNames.push('justify-content-' + justifyContent);
|
||||
|
||||
if(!alignItems && !justifyContent && center) newClassNames.push('align-items-center', 'justify-content-center');
|
||||
if(!alignItems && !justifyContent && center) newClassNames.push('items-center', 'justify-center');
|
||||
|
||||
if(classNames.length) newClassNames.push(...classNames);
|
||||
|
||||
@ -57,7 +57,7 @@ export const Grid: FC<GridProps> = props =>
|
||||
|
||||
return (
|
||||
<GridContextProvider value={ { isCssGrid: true } }>
|
||||
<Base fullHeight={ fullHeight } classNames={ getClassNames } style={ getStyle } { ...rest } />
|
||||
<Base classNames={ getClassNames } fullHeight={ fullHeight } style={ getStyle } { ...rest } />
|
||||
</GridContextProvider>
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { useVirtualizer } from '@tanstack/react-virtual';
|
||||
import { FC, Fragment, ReactElement, useEffect, useRef } from 'react';
|
||||
import { Base } from './Base';
|
||||
import { Flex } from './Flex';
|
||||
|
||||
interface InfiniteGridProps<T = any>
|
||||
@ -34,7 +33,7 @@ export const InfiniteGrid: FC<InfiniteGridProps> = props =>
|
||||
const items = virtualizer.getVirtualItems();
|
||||
|
||||
return (
|
||||
<Base innerRef={ parentRef } fit position="relative" style={ { overflowY: 'auto' } }>
|
||||
<div ref={ parentRef } className="size-full position-relative" style={ { overflowY: 'auto' } }>
|
||||
<div
|
||||
style={ {
|
||||
height: virtualizer.getTotalSize(),
|
||||
@ -54,8 +53,8 @@ export const InfiniteGrid: FC<InfiniteGridProps> = props =>
|
||||
{ items.map(virtualRow => (
|
||||
<div
|
||||
key={ virtualRow.key + 'a' }
|
||||
data-index={ virtualRow.index }
|
||||
ref={ virtualizer.measureElement }
|
||||
data-index={ virtualRow.index }
|
||||
style={ {
|
||||
display: 'grid',
|
||||
gap: '0.25rem',
|
||||
@ -79,6 +78,6 @@ export const InfiniteGrid: FC<InfiniteGridProps> = props =>
|
||||
)) }
|
||||
</Flex>
|
||||
</div>
|
||||
</Base>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ export const InfiniteScroll: FC<InfiniteScrollProps> = props =>
|
||||
const items = virtualizer.getVirtualItems();
|
||||
|
||||
return (
|
||||
<Base fit innerRef={ parentRef } position="relative" overflow="auto">
|
||||
<Base fit innerRef={ parentRef } overflow="auto" position="relative">
|
||||
<div
|
||||
style={ {
|
||||
height: virtualizer.getTotalSize(),
|
||||
@ -43,8 +43,8 @@ export const InfiniteScroll: FC<InfiniteScrollProps> = props =>
|
||||
{ items.map((virtualRow) => (
|
||||
<div
|
||||
key={ virtualRow.key }
|
||||
data-index={ virtualRow.index }
|
||||
ref={ virtualizer.measureElement }>
|
||||
ref={ virtualizer.measureElement }
|
||||
data-index={ virtualRow.index }>
|
||||
{ rowRender(rows[virtualRow.index]) }
|
||||
</div>
|
||||
)) }
|
||||
|
@ -31,15 +31,15 @@ export const NitroCardHeaderView: FC<NitroCardHeaderViewProps> = props =>
|
||||
}
|
||||
|
||||
return (
|
||||
<Column center position="relative" classNames={ getClassNames } { ...rest }>
|
||||
<Flex fullWidth center>
|
||||
<Column center classNames={ getClassNames } position="relative" { ...rest }>
|
||||
<Flex center fullWidth>
|
||||
<span className="nitro-card-header-text">{ headerText }</span>
|
||||
{ isGalleryPhoto &&
|
||||
<Base position="absolute" className="end-4 nitro-card-header-report-camera" onClick={ onReportPhoto }>
|
||||
<Base className="end-4 nitro-card-header-report-camera" position="absolute" onClick={ onReportPhoto }>
|
||||
<FaFlag className="fa-icon" />
|
||||
</Base>
|
||||
}
|
||||
<Flex center position="absolute" className="end-2 nitro-card-header-close" onMouseDownCapture={ onMouseDown } onClick={ onCloseClick }>
|
||||
<Flex center className="end-2 nitro-card-header-close" position="absolute" onClick={ onCloseClick } onMouseDownCapture={ onMouseDown }>
|
||||
<FaTimes className="fa-icon w-12 h-12" />
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
@ -1,23 +0,0 @@
|
||||
import { FC, useMemo } from 'react';
|
||||
import { Flex, FlexProps } from '..';
|
||||
|
||||
interface NitroCardSubHeaderProps extends FlexProps {
|
||||
variant?: string;
|
||||
}
|
||||
export const NitroCardSubHeaderView: FC<NitroCardSubHeaderProps> = props =>
|
||||
{
|
||||
const { justifyContent = 'center', classNames = [], variant = 'muted', ...rest } = props;
|
||||
|
||||
const getClassNames = useMemo(() =>
|
||||
{
|
||||
const newClassNames: string[] = [ 'container-fluid', 'p-1' ];
|
||||
|
||||
if(classNames.length) newClassNames.push(...classNames);
|
||||
|
||||
newClassNames.push('bg-' + variant);
|
||||
|
||||
return newClassNames;
|
||||
}, [ classNames, variant ]);
|
||||
|
||||
return <Flex justifyContent={ justifyContent } classNames={ getClassNames } { ...rest } />;
|
||||
}
|
@ -26,8 +26,8 @@ export const NitroCardView: FC<NitroCardViewProps> = props =>
|
||||
|
||||
return (
|
||||
<NitroCardContextProvider value={ { theme } }>
|
||||
<DraggableWindow uniqueKey={ uniqueKey } handleSelector={ handleSelector } windowPosition={ windowPosition } disableDrag={ disableDrag }>
|
||||
<Column innerRef={ elementRef } overflow={ overflow } position={ position } gap={ gap } classNames={ getClassNames } { ...rest } />
|
||||
<DraggableWindow disableDrag={ disableDrag } handleSelector={ handleSelector } uniqueKey={ uniqueKey } windowPosition={ windowPosition }>
|
||||
<Column classNames={ getClassNames } gap={ gap } innerRef={ elementRef } overflow={ overflow } position={ position } { ...rest } />
|
||||
</DraggableWindow>
|
||||
</NitroCardContextProvider>
|
||||
);
|
||||
|
@ -70,13 +70,13 @@ export const NitroCardAccordionSetView: FC<NitroCardAccordionSetViewProps> = pro
|
||||
|
||||
return (
|
||||
<Column classNames={ getClassNames } gap={ gap } { ...rest }>
|
||||
<Flex pointer justifyContent="between" className="nitro-card-accordion-set-header px-2 py-1" onClick={ onClick }>
|
||||
<Flex pointer className="nitro-card-accordion-set-header px-2 py-1" justifyContent="between" onClick={ onClick }>
|
||||
<Text>{ headerText }</Text>
|
||||
{ isOpen && <FaCaretUp className="fa-icon" /> }
|
||||
{ !isOpen && <FaCaretDown className="fa-icon" /> }
|
||||
</Flex>
|
||||
{ isOpen &&
|
||||
<Column fullHeight overflow="auto" gap={ 0 } className="nitro-card-accordion-set-content">
|
||||
<Column fullHeight className="nitro-card-accordion-set-content" gap={ 0 } overflow="auto">
|
||||
{ children }
|
||||
</Column> }
|
||||
</Column>
|
||||
|
@ -1,7 +1,6 @@
|
||||
export * from './accordion';
|
||||
export * from './NitroCardContentView';
|
||||
export * from './NitroCardContext';
|
||||
export * from './NitroCardHeaderView';
|
||||
export * from './NitroCardSubHeaderView';
|
||||
export * from './NitroCardView';
|
||||
export * from './accordion';
|
||||
export * from './tabs';
|
||||
|
@ -24,8 +24,8 @@ export const NitroCardTabsItemView: FC<NitroCardTabsItemViewProps> = props =>
|
||||
}, [ isActive, classNames ]);
|
||||
|
||||
return (
|
||||
<Flex overflow={ overflow } pointer={ pointer } position={ position } classNames={ getClassNames } { ...rest }>
|
||||
<Flex shrink center>
|
||||
<Flex classNames={ getClassNames } overflow={ overflow } pointer={ pointer } position={ position } { ...rest }>
|
||||
<Flex center shrink>
|
||||
{ children }
|
||||
</Flex>
|
||||
{ (count > 0) &&
|
||||
|
@ -15,7 +15,7 @@ export const NitroCardTabsView: FC<FlexProps> = props =>
|
||||
}, [ classNames ]);
|
||||
|
||||
return (
|
||||
<Flex justifyContent={ justifyContent } gap={ gap } classNames={ getClassNames } { ...rest }>
|
||||
<Flex classNames={ getClassNames } gap={ gap } justifyContent={ justifyContent } { ...rest }>
|
||||
{ children }
|
||||
</Flex>
|
||||
);
|
||||
|
@ -263,7 +263,7 @@ export const DraggableWindow: FC<DraggableWindowProps> = props =>
|
||||
|
||||
return (
|
||||
createPortal(
|
||||
<Base position="absolute" innerRef={ elementRef } className="draggable-window" onMouseDownCapture={ onMouseDown } onTouchStartCapture={ onTouchStart } style={ dragStyle }>
|
||||
<Base className="draggable-window" innerRef={ elementRef } position="absolute" style={ dragStyle } onMouseDownCapture={ onMouseDown } onTouchStartCapture={ onTouchStart }>
|
||||
{ children }
|
||||
</Base>, document.getElementById('draggable-windows-container'))
|
||||
);
|
||||
|
@ -1,7 +1,6 @@
|
||||
export * from './AutoGrid';
|
||||
export * from './Base';
|
||||
export * from './Button';
|
||||
export * from './ButtonGroup';
|
||||
export * from './Column';
|
||||
export * from './Flex';
|
||||
export * from './FormGroup';
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { FC, useMemo } from 'react';
|
||||
import { LocalizeText } from '../../api';
|
||||
import { Base, BaseProps } from '../Base';
|
||||
import { Flex } from '../Flex';
|
||||
|
||||
interface LayoutCounterTimeViewProps extends BaseProps<HTMLDivElement>
|
||||
{
|
||||
@ -25,19 +24,19 @@ export const LayoutCounterTimeView: FC<LayoutCounterTimeViewProps> = props =>
|
||||
}, [ classNames ]);
|
||||
|
||||
return (
|
||||
<Flex gap={ 1 }>
|
||||
<div className="flex gap-1 top-2 end-2">
|
||||
<Base classNames={ getClassNames } { ...rest }>
|
||||
<div>{ day != '00' ? day : hour }{ day != '00' ? LocalizeText('countdown_clock_unit_days') : LocalizeText('countdown_clock_unit_hours') }</div>
|
||||
</Base>
|
||||
<Base style={ { marginTop: '3px' } }>:</Base>
|
||||
<Base classNames={ getClassNames } { ...rest }>
|
||||
<div style={ { marginTop: '3px' } }>:</div>
|
||||
<Base className="nitro-counter-time" { ...rest }>
|
||||
<div>{ minutes }{ LocalizeText('countdown_clock_unit_minutes') }</div>
|
||||
</Base>
|
||||
<Base style={ { marginTop: '3px' } }>:</Base>
|
||||
<Base classNames={ getClassNames } { ...rest }>
|
||||
<Base className="nitro-counter-time" { ...rest }>
|
||||
<div>{ seconds }{ LocalizeText('countdown_clock_unit_seconds') }</div>
|
||||
</Base>
|
||||
{ children }
|
||||
</Flex>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -13,5 +13,5 @@ export const LayoutFurniIconImageView: FC<LayoutFurniIconImageViewProps> = props
|
||||
{
|
||||
const { productType = 's', productClassId = -1, extraData = '', ...rest } = props;
|
||||
|
||||
return <LayoutImage imageUrl={ GetImageIconUrlForProduct(productType, productClassId, extraData) } className="furni-image" { ...rest } />;
|
||||
return <LayoutImage className="furni-image" imageUrl={ GetImageIconUrlForProduct(productType, productClassId, extraData) } { ...rest } />;
|
||||
}
|
||||
|
@ -19,19 +19,19 @@ export const LayoutGiftTagView: FC<LayoutGiftTagViewProps> = props =>
|
||||
const { figure = null, userName = null, message = null, editable = false, onChange = null } = props;
|
||||
|
||||
return (
|
||||
<Flex overflow="hidden" className="nitro-gift-card text-black">
|
||||
<div className="d-flex align-items-center justify-content-center gift-face flex-shrink-0">
|
||||
<Flex className="nitro-gift-card text-black" overflow="hidden">
|
||||
<div className="flex items-center justify-center gift-face flex-shrink-0">
|
||||
{ !userName && <div className="gift-incognito"></div> }
|
||||
{ figure && <div className="gift-avatar">
|
||||
<LayoutAvatarImageView figure={ figure } direction={ 2 } headOnly={ true } />
|
||||
<LayoutAvatarImageView direction={ 2 } figure={ figure } headOnly={ true } />
|
||||
</div> }
|
||||
</div>
|
||||
<Flex overflow="hidden" className="w-100 pt-4 pb-4 pe-4 ps-3">
|
||||
<Column grow overflow="auto" justifyContent="between">
|
||||
<Flex className="w-100 pt-4 pb-4 pe-4 ps-3" overflow="hidden">
|
||||
<Column className="flex-grow-1" justifyContent="between" overflow="auto">
|
||||
{ !editable &&
|
||||
<Text textBreak className="gift-message">{ message }</Text> }
|
||||
{ editable && (onChange !== null) &&
|
||||
<textarea className="gift-message h-100" maxLength={ 140 } value={ message } onChange={ (e) => onChange(e.target.value) } placeholder={ LocalizeText('catalog.gift_wrapping_new.message_hint') }></textarea> }
|
||||
<textarea className="gift-message h-100" maxLength={ 140 } placeholder={ LocalizeText('catalog.gift_wrapping_new.message_hint') } value={ message } onChange={ (e) => onChange(e.target.value) }></textarea> }
|
||||
{ userName &&
|
||||
<Text italics textEnd className="pe-1">{ LocalizeText('catalog.gift_wrapping_new.message_from', [ 'name' ], [ userName ]) }</Text> }
|
||||
</Column>
|
||||
|
@ -59,7 +59,7 @@ export const LayoutGridItem: FC<LayoutGridItemProps> = props =>
|
||||
}, [ style, itemImage, itemColor, itemUniqueSoldout, itemUniqueNumber ]);
|
||||
|
||||
return (
|
||||
<Column center={ center } pointer position={ position } overflow={ overflow } column={ column } classNames={ getClassNames } style={ getStyle } { ...rest }>
|
||||
<Column pointer center={ center } classNames={ getClassNames } column={ column } overflow={ overflow } position={ position } style={ getStyle } { ...rest }>
|
||||
{ (itemCount > itemCountMinimum) &&
|
||||
<LayoutItemCountView count={ itemCount } /> }
|
||||
{ (itemUniqueNumber > 0) &&
|
||||
|
@ -9,5 +9,5 @@ export const LayoutImage: FC<LayoutImageProps> = props =>
|
||||
{
|
||||
const { imageUrl = null, className = '', ...rest } = props;
|
||||
|
||||
return <img src={ imageUrl } className={ 'no-select ' + className } alt="" { ...rest } />;
|
||||
return <img alt="" className={ 'no-select ' + className } src={ imageUrl } { ...rest } />;
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ export const LayoutItemCountView: FC<LayoutItemCountViewProps> = props =>
|
||||
}, [ classNames ]);
|
||||
|
||||
return (
|
||||
<Base position="absolute" classNames={ getClassNames } { ...rest }>
|
||||
<Base classNames={ getClassNames } position="absolute" { ...rest }>
|
||||
{ count }
|
||||
{ children }
|
||||
</Base>
|
||||
|
@ -34,7 +34,7 @@ export const LayoutMiniCameraView: FC<LayoutMiniCameraViewProps> = props =>
|
||||
<DraggableWindow handleSelector=".nitro-room-thumbnail-camera">
|
||||
<div className="nitro-room-thumbnail-camera px-2">
|
||||
<div ref={ elementRef } className={ 'camera-frame' } />
|
||||
<div className="d-flex align-items-end h-100 pb-2">
|
||||
<div className="flex align-items-end h-100 pb-2">
|
||||
<button className="btn btn-sm btn-danger w-100 mb-1 me-2" onClick={ onClose }>{ LocalizeText('cancel') }</button>
|
||||
<button className="btn btn-sm btn-success w-100 mb-1" onClick={ takePicture }>{ LocalizeText('navigator.thumbeditor.save') }</button>
|
||||
</div>
|
||||
|
@ -27,7 +27,7 @@ export const LayoutNotificationAlertView: FC<LayoutNotificationAlertViewProps> =
|
||||
return (
|
||||
<NitroCardView classNames={ getClassNames } theme="primary-slim" { ...rest }>
|
||||
<NitroCardHeaderView headerText={ title } onCloseClick={ onClose } />
|
||||
<NitroCardContentView grow justifyContent="between" overflow="hidden" className="text-black" gap={ 0 }>
|
||||
<NitroCardContentView grow className="text-black" gap={ 0 } justifyContent="between" overflow="hidden">
|
||||
{ children }
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
|
@ -45,8 +45,8 @@ export const LayoutNotificationBubbleView: FC<LayoutNotificationBubbleViewProps>
|
||||
}, [ fadesOut, timeoutMs, onClose ]);
|
||||
|
||||
return (
|
||||
<TransitionAnimation type={ TransitionAnimationTypes.FADE_IN } inProp={ isVisible } timeout={ 300 }>
|
||||
<Flex overflow={ overflow } classNames={ getClassNames } onClick={ onClose } { ...rest } />
|
||||
<TransitionAnimation inProp={ isVisible } timeout={ 300 } type={ TransitionAnimationTypes.FADE_IN }>
|
||||
<Flex classNames={ getClassNames } overflow={ overflow } onClick={ onClose } { ...rest } />
|
||||
</TransitionAnimation>
|
||||
);
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ export const LayoutPrizeProductImageView: FC<LayoutPrizeProductImageViewProps> =
|
||||
{
|
||||
case ProductTypeEnum.WALL:
|
||||
case ProductTypeEnum.FLOOR:
|
||||
return <LayoutFurniImageView productType={ productType } productClassId={ classId } />
|
||||
return <LayoutFurniImageView productClassId={ classId } productType={ productType } />
|
||||
case ProductTypeEnum.BADGE:
|
||||
return <LayoutBadgeImageView badgeCode={ extraParam }/>
|
||||
case ProductTypeEnum.HABBO_CLUB:
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { FC, useMemo } from 'react';
|
||||
import { Base } from '../Base';
|
||||
import { Column, ColumnProps } from '../Column';
|
||||
import { Flex } from '../Flex';
|
||||
|
||||
interface LayoutProgressBarProps extends ColumnProps
|
||||
{
|
||||
@ -24,10 +22,10 @@ export const LayoutProgressBar: FC<LayoutProgressBarProps> = props =>
|
||||
}, [ classNames ]);
|
||||
|
||||
return (
|
||||
<Column position={ position } justifyContent={ justifyContent } classNames={ getClassNames } { ...rest }>
|
||||
<Column classNames={ getClassNames } justifyContent={ justifyContent } position={ position } { ...rest }>
|
||||
{ text && (text.length > 0) &&
|
||||
<Flex fit center position="absolute" className="nitro-progress-bar-text small">{ text }</Flex> }
|
||||
<Base className="nitro-progress-bar-inner" style={ { width: (~~((((progress - 0) * (100 - 0)) / (maxProgress - 0)) + 0) + '%') } } />
|
||||
<div className="flex items-center justify-center size-full p-absolute nitro-progress-bar-text small">{ text }</div> }
|
||||
<div className="nitro-progress-bar-inner" style={ { width: (~~((((progress - 0) * (100 - 0)) / (maxProgress - 0)) + 0) + '%') } } />
|
||||
{ children }
|
||||
</Column>
|
||||
);
|
||||
|
@ -29,7 +29,7 @@ export const LayoutRoomThumbnailView: FC<LayoutRoomThumbnailViewProps> = props =
|
||||
}, [ customUrl, roomId ]);
|
||||
|
||||
return (
|
||||
<Base shrink={ shrink } overflow={ overflow } classNames={ getClassNames } { ...rest }>
|
||||
<Base classNames={ getClassNames } overflow={ overflow } shrink={ shrink } { ...rest }>
|
||||
{ getImageUrl && <img alt="" src={ getImageUrl } /> }
|
||||
{ children }
|
||||
</Base>
|
||||
|
@ -22,9 +22,9 @@ export const LayoutTrophyView: FC<LayoutTrophyViewProps> = props =>
|
||||
|
||||
return (
|
||||
<DraggableWindow handleSelector=".drag-handler">
|
||||
<Column gap={ 0 } alignItems="center" className={ `nitro-layout-trophy trophy-${ color }` }>
|
||||
<Flex center fullWidth position="relative" className="trophy-header drag-handler">
|
||||
<Base position="absolute" pointer className="trophy-close" onClick={ onCloseClick } />
|
||||
<Column alignItems="center" className={ `nitro-layout-trophy trophy-${ color }` } gap={ 0 }>
|
||||
<Flex center fullWidth className="trophy-header drag-handler" position="relative">
|
||||
<Base pointer className="trophy-close" position="absolute" onClick={ onCloseClick } />
|
||||
<Text bold>{ LocalizeText('widget.furni.trophy.title') }</Text>
|
||||
</Flex>
|
||||
<Column className="trophy-content py-1" gap={ 1 }>
|
||||
@ -32,7 +32,7 @@ export const LayoutTrophyView: FC<LayoutTrophyViewProps> = props =>
|
||||
<Text bold>{ customTitle }</Text> }
|
||||
{ message }
|
||||
</Column>
|
||||
<Flex alignItems="center" justifyContent="between" className="trophy-footer mt-1">
|
||||
<Flex alignItems="center" className="trophy-footer mt-1" justifyContent="between">
|
||||
<Text bold>{ date }</Text>
|
||||
<Text bold>{ senderName }</Text>
|
||||
</Flex>
|
||||
|
@ -2,7 +2,6 @@ import { FC, useMemo } from 'react';
|
||||
import { LocalizeText } from '../../../api';
|
||||
import { Base, BaseProps } from '../../Base';
|
||||
import { Column } from '../../Column';
|
||||
import { Flex } from '../../Flex';
|
||||
import { LayoutLimitedEditionStyledNumberView } from './LayoutLimitedEditionStyledNumberView';
|
||||
|
||||
interface LayoutLimitedEditionCompletePlateViewProps extends BaseProps<HTMLDivElement>
|
||||
@ -27,14 +26,14 @@ export const LayoutLimitedEditionCompletePlateView: FC<LayoutLimitedEditionCompl
|
||||
return (
|
||||
<Base classNames={ getClassNames } { ...rest }>
|
||||
<Column className="plate-container" gap={ 0 }>
|
||||
<Flex justifyContent="between" alignItems="center">
|
||||
<div className="flex justify-content-between items-center">
|
||||
{ LocalizeText('unique.items.left') }
|
||||
<div><LayoutLimitedEditionStyledNumberView value={ uniqueLimitedItemsLeft } /></div>
|
||||
</Flex>
|
||||
<Flex justifyContent="between" alignItems="center">
|
||||
</div>
|
||||
<div className="flex justify-content-between items-center">
|
||||
{ LocalizeText('unique.items.number.sold') }
|
||||
<div><LayoutLimitedEditionStyledNumberView value={ uniqueLimitedSeriesSize } /></div>
|
||||
</Flex>
|
||||
</div>
|
||||
</Column>
|
||||
</Base>
|
||||
);
|
||||
|
@ -1,31 +1,31 @@
|
||||
import { AddLinkEventTracker, GetCommunication, HabboWebTools, ILinkEventTracker, RemoveLinkEventTracker, RoomSessionEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { Base, TransitionAnimation, TransitionAnimationTypes } from '../../common';
|
||||
import { useNitroEvent } from '../../hooks';
|
||||
import { AchievementsView } from '../achievements/AchievementsView';
|
||||
import { AvatarEditorView } from '../avatar-editor';
|
||||
import { CameraWidgetView } from '../camera/CameraWidgetView';
|
||||
import { CampaignView } from '../campaign/CampaignView';
|
||||
import { CatalogView } from '../catalog/CatalogView';
|
||||
import { ChatHistoryView } from '../chat-history/ChatHistoryView';
|
||||
import { FloorplanEditorView } from '../floorplan-editor/FloorplanEditorView';
|
||||
import { FriendsView } from '../friends/FriendsView';
|
||||
import { GameCenterView } from '../game-center/GameCenterView';
|
||||
import { GroupsView } from '../groups/GroupsView';
|
||||
import { GuideToolView } from '../guide-tool/GuideToolView';
|
||||
import { HcCenterView } from '../hc-center/HcCenterView';
|
||||
import { HelpView } from '../help/HelpView';
|
||||
import { HotelView } from '../hotel-view/HotelView';
|
||||
import { InventoryView } from '../inventory/InventoryView';
|
||||
import { ModToolsView } from '../mod-tools/ModToolsView';
|
||||
import { NavigatorView } from '../navigator/NavigatorView';
|
||||
import { NitropediaView } from '../nitropedia/NitropediaView';
|
||||
import { RightSideView } from '../right-side/RightSideView';
|
||||
import { RoomView } from '../room/RoomView';
|
||||
import { ToolbarView } from '../toolbar/ToolbarView';
|
||||
import { UserProfileView } from '../user-profile/UserProfileView';
|
||||
import { UserSettingsView } from '../user-settings/UserSettingsView';
|
||||
import { WiredView } from '../wired/WiredView';
|
||||
import { TransitionAnimation, TransitionAnimationTypes } from '../common';
|
||||
import { useNitroEvent } from '../hooks';
|
||||
import { AchievementsView } from './achievements/AchievementsView';
|
||||
import { AvatarEditorView } from './avatar-editor';
|
||||
import { CameraWidgetView } from './camera/CameraWidgetView';
|
||||
import { CampaignView } from './campaign/CampaignView';
|
||||
import { CatalogView } from './catalog/CatalogView';
|
||||
import { ChatHistoryView } from './chat-history/ChatHistoryView';
|
||||
import { FloorplanEditorView } from './floorplan-editor/FloorplanEditorView';
|
||||
import { FriendsView } from './friends/FriendsView';
|
||||
import { GameCenterView } from './game-center/GameCenterView';
|
||||
import { GroupsView } from './groups/GroupsView';
|
||||
import { GuideToolView } from './guide-tool/GuideToolView';
|
||||
import { HcCenterView } from './hc-center/HcCenterView';
|
||||
import { HelpView } from './help/HelpView';
|
||||
import { HotelView } from './hotel-view/HotelView';
|
||||
import { InventoryView } from './inventory/InventoryView';
|
||||
import { ModToolsView } from './mod-tools/ModToolsView';
|
||||
import { NavigatorView } from './navigator/NavigatorView';
|
||||
import { NitropediaView } from './nitropedia/NitropediaView';
|
||||
import { RightSideView } from './right-side/RightSideView';
|
||||
import { RoomView } from './room/RoomView';
|
||||
import { ToolbarView } from './toolbar/ToolbarView';
|
||||
import { UserProfileView } from './user-profile/UserProfileView';
|
||||
import { UserSettingsView } from './user-settings/UserSettingsView';
|
||||
import { WiredView } from './wired/WiredView';
|
||||
|
||||
export const MainView: FC<{}> = props =>
|
||||
{
|
||||
@ -79,8 +79,8 @@ export const MainView: FC<{}> = props =>
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Base fit>
|
||||
<TransitionAnimation type={ TransitionAnimationTypes.FADE_IN } inProp={ landingViewVisible } timeout={ 300 }>
|
||||
<>
|
||||
<TransitionAnimation inProp={ landingViewVisible } timeout={ 300 } type={ TransitionAnimationTypes.FADE_IN }>
|
||||
<HotelView />
|
||||
</TransitionAnimation>
|
||||
<ToolbarView isInRoom={ !landingViewVisible } />
|
||||
@ -106,6 +106,6 @@ export const MainView: FC<{}> = props =>
|
||||
<CampaignView />
|
||||
<GameCenterView />
|
||||
<FloorplanEditorView />
|
||||
</Base>
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import { AddLinkEventTracker, ILinkEventTracker, RemoveLinkEventTracker } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { AchievementUtilities, LocalizeText } from '../../api';
|
||||
import { Base, Column, LayoutImage, LayoutProgressBar, NitroCardContentView, NitroCardHeaderView, NitroCardSubHeaderView, NitroCardView, Text } from '../../common';
|
||||
import { Column, LayoutImage, LayoutProgressBar, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../common';
|
||||
import { useAchievements } from '../../hooks';
|
||||
import { AchievementCategoryView } from './views/AchievementCategoryView';
|
||||
import { AchievementsCategoryListView } from './views/category-list/AchievementsCategoryListView';
|
||||
@ -44,24 +44,24 @@ export const AchievementsView: FC<{}> = props =>
|
||||
if(!isVisible) return null;
|
||||
|
||||
return (
|
||||
<NitroCardView uniqueKey="achievements" className="nitro-achievements" theme="primary-slim">
|
||||
<NitroCardView className="nitro-achievements" theme="primary-slim" uniqueKey="achievements">
|
||||
<NitroCardHeaderView headerText={ LocalizeText('inventory.achievements') } onCloseClick={ event => setIsVisible(false) } />
|
||||
{ selectedCategory &&
|
||||
<NitroCardSubHeaderView position="relative" className="justify-content-center align-items-center cursor-pointer" gap={ 3 }>
|
||||
<Base onClick={ event => setSelectedCategoryCode(null) } className="nitro-achievements-back-arrow" />
|
||||
<Column grow gap={ 0 }>
|
||||
<Text fontSize={ 4 } fontWeight="bold" className="text-small">{ LocalizeText(`quests.${ selectedCategory.code }.name`) }</Text>
|
||||
<div className="position-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>
|
||||
<Text>{ LocalizeText('achievements.details.categoryprogress', [ 'progress', 'limit' ], [ selectedCategory.getProgress().toString(), selectedCategory.getMaxProgress().toString() ]) }</Text>
|
||||
</Column>
|
||||
<LayoutImage imageUrl={ AchievementUtilities.getAchievementCategoryImageUrl(selectedCategory, null,true) } />
|
||||
</NitroCardSubHeaderView> }
|
||||
</div> }
|
||||
<NitroCardContentView gap={ 1 }>
|
||||
{ !selectedCategory &&
|
||||
<>
|
||||
<AchievementsCategoryListView categories={ achievementCategories } selectedCategoryCode={ selectedCategoryCode } setSelectedCategoryCode={ setSelectedCategoryCode } />
|
||||
<Column grow justifyContent="end" gap={ 1 }>
|
||||
<Text small center>{ LocalizeText('achievements.categories.score', [ 'score' ], [ achievementScore.toString() ]) }</Text>
|
||||
<LayoutProgressBar text={ LocalizeText('achievements.categories.totalprogress', [ 'progress', 'limit' ], [ getProgress.toString(), getMaxProgress.toString() ]) } progress={ getProgress } maxProgress={ getMaxProgress } />
|
||||
<Column className="flex-grow-1" gap={ 1 } justifyContent="end">
|
||||
<Text center small>{ LocalizeText('achievements.categories.score', [ 'score' ], [ achievementScore.toString() ]) }</Text>
|
||||
<LayoutProgressBar maxProgress={ getMaxProgress } progress={ getProgress } text={ LocalizeText('achievements.categories.totalprogress', [ 'progress', 'limit' ], [ getProgress.toString(), getMaxProgress.toString() ]) } />
|
||||
</Column>
|
||||
</> }
|
||||
{ selectedCategory &&
|
||||
|
@ -18,24 +18,24 @@ export const AchievementDetailsView: FC<AchievementDetailsViewProps> = props =>
|
||||
return (
|
||||
<Flex shrink className="bg-muted rounded p-2 text-black" gap={ 2 } overflow="hidden">
|
||||
<Column center gap={ 1 }>
|
||||
<AchievementBadgeView className="nitro-achievements-badge-image" achievement={ achievement } scale={ 2 } />
|
||||
<AchievementBadgeView achievement={ achievement } className="nitro-achievements-badge-image" scale={ 2 } />
|
||||
<Text fontWeight="bold">
|
||||
{ LocalizeText('achievements.details.level', [ 'level', 'limit' ], [ AchievementUtilities.getAchievementLevel(achievement).toString(), achievement.levelCount.toString() ]) }
|
||||
</Text>
|
||||
</Column>
|
||||
<Column fullWidth justifyContent="center" overflow="hidden">
|
||||
<Column gap={ 1 }>
|
||||
<Text fontWeight="bold" truncate>
|
||||
<div className="flex flex-column gap-1">
|
||||
<Text truncate fontWeight="bold">
|
||||
{ LocalizeBadgeName(AchievementUtilities.getAchievementBadgeCode(achievement)) }
|
||||
</Text>
|
||||
<Text textBreak>
|
||||
{ LocalizeBadgeDescription(AchievementUtilities.getAchievementBadgeCode(achievement)) }
|
||||
</Text>
|
||||
</Column>
|
||||
</div>
|
||||
{ ((achievement.levelRewardPoints > 0) || (achievement.scoreLimit > 0)) &&
|
||||
<Column gap={ 1 }>
|
||||
<div className="flex flex-column gap-1">
|
||||
{ (achievement.levelRewardPoints > 0) &&
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
<div className="flex items-center gap-1">
|
||||
<Text truncate className="small">
|
||||
{ LocalizeText('achievements.details.reward') }
|
||||
</Text>
|
||||
@ -43,10 +43,10 @@ export const AchievementDetailsView: FC<AchievementDetailsViewProps> = props =>
|
||||
{ achievement.levelRewardPoints }
|
||||
<LayoutCurrencyIcon type={ achievement.levelRewardPointType } />
|
||||
</Flex>
|
||||
</Flex> }
|
||||
</div> }
|
||||
{ (achievement.scoreLimit > 0) &&
|
||||
<LayoutProgressBar text={ LocalizeText('achievements.details.progress', [ 'progress', 'limit' ], [ (achievement.currentPoints + achievement.scoreAtStartOfLevel).toString(), (achievement.scoreLimit + achievement.scoreAtStartOfLevel).toString() ]) } progress={ (achievement.currentPoints + achievement.scoreAtStartOfLevel) } maxProgress={ (achievement.scoreLimit + achievement.scoreAtStartOfLevel) } /> }
|
||||
</Column> }
|
||||
<LayoutProgressBar maxProgress={ (achievement.scoreLimit + achievement.scoreAtStartOfLevel) } progress={ (achievement.currentPoints + achievement.scoreAtStartOfLevel) } text={ LocalizeText('achievements.details.progress', [ 'progress', 'limit' ], [ (achievement.currentPoints + achievement.scoreAtStartOfLevel).toString(), (achievement.scoreLimit + achievement.scoreAtStartOfLevel).toString() ]) } /> }
|
||||
</div> }
|
||||
</Column>
|
||||
</Flex>
|
||||
)
|
||||
|
@ -13,7 +13,7 @@ export const AchievementListView: FC<AchievementListViewProps> = props =>
|
||||
const { achievements = null } = props;
|
||||
|
||||
return (
|
||||
<AutoGrid columnCount={ 6 } columnMinWidth={ 50 } columnMinHeight={ 50 }>
|
||||
<AutoGrid columnCount={ 6 } columnMinHeight={ 50 } columnMinWidth={ 50 }>
|
||||
{ achievements && (achievements.length > 0) && achievements.map((achievement, index) => <AchievementListItemView key={ index } achievement={ achievement } />) }
|
||||
</AutoGrid>
|
||||
);
|
||||
|
@ -21,10 +21,10 @@ export const AchievementsCategoryListItemView: FC<AchievementCategoryListItemVie
|
||||
const getTotalUnseen = AchievementUtilities.getAchievementCategoryTotalUnseen(category);
|
||||
|
||||
return (
|
||||
<LayoutGridItem itemActive={ (selectedCategoryCode === category.code) } itemCount={ getTotalUnseen } itemCountMinimum={ 0 } gap={ 1 } onClick={ event => setSelectedCategoryCode(category.code) }>
|
||||
<Text fullWidth center small className="pt-1">{ LocalizeText(`quests.${ category.code }.name`) }</Text>
|
||||
<LayoutBackgroundImage position="relative" imageUrl={ getCategoryImage }>
|
||||
<Text fullWidth center position="absolute" variant="white" style={ { fontSize: 12, bottom: 9 } }>{ progress } / { maxProgress }</Text>
|
||||
<LayoutGridItem gap={ 1 } itemActive={ (selectedCategoryCode === category.code) } itemCount={ getTotalUnseen } itemCountMinimum={ 0 } onClick={ event => setSelectedCategoryCode(category.code) }>
|
||||
<Text center fullWidth small className="pt-1">{ LocalizeText(`quests.${ category.code }.name`) }</Text>
|
||||
<LayoutBackgroundImage imageUrl={ getCategoryImage } position="relative">
|
||||
<Text center fullWidth position="absolute" style={ { fontSize: 12, bottom: 9 } } variant="white">{ progress } / { maxProgress }</Text>
|
||||
</LayoutBackgroundImage>
|
||||
</LayoutGridItem>
|
||||
);
|
||||
|
@ -15,7 +15,7 @@ export const AchievementsCategoryListView: FC<AchievementsCategoryListViewProps>
|
||||
const { categories = null, selectedCategoryCode = null, setSelectedCategoryCode = null } = props;
|
||||
|
||||
return (
|
||||
<AutoGrid columnCount={ 3 } columnMinWidth={ 90 } columnMinHeight={ 100 }>
|
||||
<AutoGrid columnCount={ 3 } columnMinHeight={ 100 } columnMinWidth={ 90 }>
|
||||
{ categories && (categories.length > 0) && categories.map((category, index) => <AchievementsCategoryListItemView key={ index } category={ category } selectedCategoryCode={ selectedCategoryCode } setSelectedCategoryCode={ setSelectedCategoryCode } /> ) }
|
||||
</AutoGrid>
|
||||
);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { AvatarDirectionAngle } from '@nitrots/nitro-renderer';
|
||||
import { FC, useState } from 'react';
|
||||
import { Base, Column, LayoutAvatarImageView } from '../../common';
|
||||
import { LayoutAvatarImageView } from '../../common';
|
||||
import { useAvatarEditor } from '../../hooks';
|
||||
import { AvatarEditorIcon } from './AvatarEditorIcon';
|
||||
|
||||
@ -27,14 +27,14 @@ export const AvatarEditorFigurePreviewView: FC<{}> = props =>
|
||||
}
|
||||
|
||||
return (
|
||||
<Column className="figure-preview-container" overflow="hidden" position="relative">
|
||||
<LayoutAvatarImageView figure={ getFigureString } direction={ direction } scale={ 2 } />
|
||||
<div className="flex flex-column figure-preview-container overflow-hidden position-relative">
|
||||
<LayoutAvatarImageView direction={ direction } figure={ getFigureString } scale={ 2 } />
|
||||
<AvatarEditorIcon className="avatar-spotlight" icon="spotlight" />
|
||||
<Base className="avatar-shadow" />
|
||||
<Base className="arrow-container">
|
||||
<AvatarEditorIcon pointer icon="arrow-left" onClick={ event => rotateFigure(direction + 1) } />
|
||||
<AvatarEditorIcon pointer icon="arrow-right" onClick={ event => rotateFigure(direction - 1) } />
|
||||
</Base>
|
||||
</Column>
|
||||
<div className="avatar-shadow" />
|
||||
<div className="arrow-container">
|
||||
<AvatarEditorIcon icon="arrow-left" onClick={ event => rotateFigure(direction + 1) } />
|
||||
<AvatarEditorIcon icon="arrow-right" onClick={ event => rotateFigure(direction - 1) } />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,27 +1,27 @@
|
||||
import { FC, useMemo } from 'react';
|
||||
import { Base, BaseProps } from '../../common';
|
||||
import { DetailedHTMLProps, HTMLAttributes, PropsWithChildren, forwardRef } from 'react';
|
||||
import { classNames } from '../../common';
|
||||
|
||||
type AvatarIconType = 'male' | 'female' | 'clear' | 'sellable' | string;
|
||||
|
||||
export const AvatarEditorIcon: FC<{
|
||||
export const AvatarEditorIcon = forwardRef<HTMLDivElement, PropsWithChildren<{
|
||||
icon: AvatarIconType;
|
||||
selected?: boolean;
|
||||
} & BaseProps<HTMLDivElement>> = props =>
|
||||
}> & DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>>((props, ref) =>
|
||||
{
|
||||
const { icon = null, selected = false, classNames = [], children = null, ...rest } = props;
|
||||
const { icon = null, selected = false, className = null, ...rest } = props;
|
||||
|
||||
const getClassNames = useMemo(() =>
|
||||
{
|
||||
const newClassNames: string[] = [ 'nitro-avatar-editor-spritesheet' ];
|
||||
return (
|
||||
<div
|
||||
ref={ ref }
|
||||
className={ classNames(
|
||||
'nitro-avatar-editor-spritesheet',
|
||||
'cursor-pointer',
|
||||
`${ icon }-icon`,
|
||||
selected && 'selected',
|
||||
className
|
||||
) }
|
||||
{ ...rest } />
|
||||
);
|
||||
});
|
||||
|
||||
if(icon && icon.length) newClassNames.push(icon + '-icon');
|
||||
|
||||
if(selected) newClassNames.push('selected');
|
||||
|
||||
if(classNames.length) newClassNames.push(...classNames);
|
||||
|
||||
return newClassNames;
|
||||
}, [ icon, selected, classNames ]);
|
||||
|
||||
return <Base classNames={ getClassNames } { ...rest } />
|
||||
}
|
||||
AvatarEditorIcon.displayName = 'AvatarEditorIcon';
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { AvatarEditorFigureCategory, AvatarFigurePartType, FigureDataContainer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { IAvatarEditorCategory } from '../../api';
|
||||
import { Column, Flex, Grid } from '../../common';
|
||||
import { useAvatarEditor } from '../../hooks';
|
||||
import { AvatarEditorIcon } from './AvatarEditorIcon';
|
||||
import { AvatarEditorFigureSetView } from './figure-set';
|
||||
@ -47,35 +46,35 @@ export const AvatarEditorModelView: FC<{
|
||||
if(!activeCategory) return null;
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
<Column size={ 2 }>
|
||||
<div className="flex gap-2 overflow-hidden">
|
||||
<div className="flex flex-column col-2">
|
||||
{ (name === AvatarEditorFigureCategory.GENERIC) &&
|
||||
<>
|
||||
<Flex center pointer className="category-item" onClick={ event => setGender(AvatarFigurePartType.MALE) }>
|
||||
<div className="category-item items-center justify-center cursor-pointer flex" onClick={ event => setGender(AvatarFigurePartType.MALE) }>
|
||||
<AvatarEditorIcon icon="male" selected={ gender === FigureDataContainer.MALE } />
|
||||
</Flex>
|
||||
<Flex center pointer className="category-item" onClick={ event => setGender(AvatarFigurePartType.FEMALE) }>
|
||||
</div>
|
||||
<div className="category-item items-center justify-center cursor-pointer flex" onClick={ event => setGender(AvatarFigurePartType.FEMALE) }>
|
||||
<AvatarEditorIcon icon="female" selected={ gender === FigureDataContainer.FEMALE } />
|
||||
</Flex>
|
||||
</div>
|
||||
</> }
|
||||
{ (name !== AvatarEditorFigureCategory.GENERIC) && (categories.length > 0) && categories.map(category =>
|
||||
{
|
||||
return (
|
||||
<Flex center pointer key={ category.setType } className="category-item" onClick={ event => selectSet(category.setType) }>
|
||||
<div key={ category.setType } className="category-item items-center justify-center cursor-pointer flex" onClick={ event => selectSet(category.setType) }>
|
||||
<AvatarEditorIcon icon={ category.setType } selected={ (activeSetType === category.setType) } />
|
||||
</Flex>
|
||||
</div>
|
||||
);
|
||||
}) }
|
||||
</Column>
|
||||
<Column size={ 5 } overflow="hidden">
|
||||
<AvatarEditorFigureSetView category={ activeCategory } />
|
||||
</Column>
|
||||
<Column size={ 5 } overflow="hidden">
|
||||
</div>
|
||||
<div className="flex flex-column overflow-hidden col-5">
|
||||
<AvatarEditorFigureSetView category={ activeCategory } columnCount={ 3 } />
|
||||
</div>
|
||||
<div className="flex flex-column overflow-hidden col-5">
|
||||
{ (maxPaletteCount >= 1) &&
|
||||
<AvatarEditorPaletteSetView category={ activeCategory } paletteIndex={ 0 } /> }
|
||||
<AvatarEditorPaletteSetView category={ activeCategory } columnCount={ 3 } paletteIndex={ 0 } /> }
|
||||
{ (maxPaletteCount === 2) &&
|
||||
<AvatarEditorPaletteSetView category={ activeCategory } paletteIndex={ 1 } /> }
|
||||
</Column>
|
||||
</Grid>
|
||||
<AvatarEditorPaletteSetView category={ activeCategory } columnCount={ 3 } paletteIndex={ 1 } /> }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import { AddLinkEventTracker, AvatarEditorFigureCategory, GetSessionDataManager,
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { FaDice, FaRedo, FaTrash } from 'react-icons/fa';
|
||||
import { AvatarEditorAction, LocalizeText, SendMessageComposer } from '../../api';
|
||||
import { Button, ButtonGroup, Column, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../common';
|
||||
import { Button, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../common';
|
||||
import { useAvatarEditor } from '../../hooks';
|
||||
import { AvatarEditorFigurePreviewView } from './AvatarEditorFigurePreviewView';
|
||||
import { AvatarEditorModelView } from './AvatarEditorModelView';
|
||||
@ -74,7 +74,7 @@ export const AvatarEditorView: FC<{}> = props =>
|
||||
if(!isVisible) return null;
|
||||
|
||||
return (
|
||||
<NitroCardView uniqueKey="avatar-editor" className="nitro-avatar-editor">
|
||||
<NitroCardView className="nitro-avatar-editor" uniqueKey="avatar-editor">
|
||||
<NitroCardHeaderView headerText={ LocalizeText('avatareditor.title') } onCloseClick={ event => setIsVisible(false) } />
|
||||
<NitroCardTabsView>
|
||||
{ Object.keys(avatarModels).map(modelKey =>
|
||||
@ -89,17 +89,17 @@ export const AvatarEditorView: FC<{}> = props =>
|
||||
}) }
|
||||
</NitroCardTabsView>
|
||||
<NitroCardContentView>
|
||||
<Grid>
|
||||
<Column size={ 9 } overflow="hidden">
|
||||
<div className="grid gap-2 overflow-hidden">
|
||||
<div className="flex flex-column col-9 overflow-hidden">
|
||||
{ ((activeModelKey.length > 0) && (activeModelKey !== AvatarEditorFigureCategory.WARDROBE)) &&
|
||||
<AvatarEditorModelView name={ activeModelKey } categories={ avatarModels[activeModelKey] } /> }
|
||||
<AvatarEditorModelView categories={ avatarModels[activeModelKey] } name={ activeModelKey } /> }
|
||||
{ (activeModelKey === AvatarEditorFigureCategory.WARDROBE) &&
|
||||
<AvatarEditorWardrobeView /> }
|
||||
</Column>
|
||||
<Column size={ 3 } overflow="hidden">
|
||||
</div>
|
||||
<div className="flex flex-column col-3 overflow-hidden gap-1">
|
||||
<AvatarEditorFigurePreviewView />
|
||||
<Column grow gap={ 1 }>
|
||||
<ButtonGroup>
|
||||
<div className="flex flex-column flex-grow-1 gap-1">
|
||||
<div className="btn-group">
|
||||
<Button variant="secondary" onClick={ event => processAction(AvatarEditorAction.ACTION_RESET) }>
|
||||
<FaRedo className="fa-icon" />
|
||||
</Button>
|
||||
@ -109,13 +109,13 @@ export const AvatarEditorView: FC<{}> = props =>
|
||||
<Button variant="secondary" onClick={ event => processAction(AvatarEditorAction.ACTION_RANDOMIZE) }>
|
||||
<FaDice className="fa-icon" />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
<Button className="w-100" variant="success" onClick={ event => processAction(AvatarEditorAction.ACTION_SAVE) }>
|
||||
{ LocalizeText('avatareditor.save') }
|
||||
</Button>
|
||||
</Column>
|
||||
</Column>
|
||||
</Grid>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { GetAvatarRenderManager, IAvatarFigureContainer, SaveWardrobeOutfitMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback } from 'react';
|
||||
import { GetClubMemberLevel, GetConfigurationValue, LocalizeText, SendMessageComposer } from '../../api';
|
||||
import { Base, Button, Flex, InfiniteGrid, LayoutAvatarImageView, LayoutCurrencyIcon, LayoutGridItem } from '../../common';
|
||||
import { Button, InfiniteGrid, LayoutAvatarImageView, LayoutCurrencyIcon, LayoutGridItem } from '../../common';
|
||||
import { useAvatarEditor } from '../../hooks';
|
||||
|
||||
export const AvatarEditorWardrobeView: FC<{}> = props =>
|
||||
@ -34,7 +34,7 @@ export const AvatarEditorWardrobeView: FC<{}> = props =>
|
||||
}, [ getFigureString, gender, savedFigures, setSavedFigures ]);
|
||||
|
||||
return (
|
||||
<InfiniteGrid rows={ savedFigures } columnCount={ 5 } overscan={ 5 } estimateSize={ 140 } itemRender={ (item: [ IAvatarFigureContainer, string ], index: number) =>
|
||||
<InfiniteGrid columnCount={ 5 } estimateSize={ 140 } itemRender={ (item: [ IAvatarFigureContainer, string ], index: number) =>
|
||||
{
|
||||
const [ figureContainer, gender ] = item;
|
||||
|
||||
@ -43,18 +43,18 @@ export const AvatarEditorWardrobeView: FC<{}> = props =>
|
||||
if(figureContainer) clubLevel = GetAvatarRenderManager().getFigureClubLevel(figureContainer, gender);
|
||||
|
||||
return (
|
||||
<LayoutGridItem position="relative" overflow="hidden" className="nitro-avatar-editor-wardrobe-figure-preview">
|
||||
<LayoutGridItem className="nitro-avatar-editor-wardrobe-figure-preview" overflow="hidden" position="relative">
|
||||
{ figureContainer &&
|
||||
<LayoutAvatarImageView figure={ figureContainer.getFigureString() } gender={ gender } direction={ 2 } /> }
|
||||
<Base className="avatar-shadow" />
|
||||
<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" /> }
|
||||
<Flex gap={ 1 } className="button-container">
|
||||
<Button variant="link" fullWidth onClick={ event => saveFigureAtWardrobeIndex(index) }>{ LocalizeText('avatareditor.wardrobe.save') }</Button>
|
||||
<div className="flex gap-1 button-container">
|
||||
<Button fullWidth variant="link" onClick={ event => saveFigureAtWardrobeIndex(index) }>{ LocalizeText('avatareditor.wardrobe.save') }</Button>
|
||||
{ figureContainer &&
|
||||
<Button variant="link" fullWidth onClick={ event => wearFigureAtIndex(index) } disabled={ (clubLevel > GetClubMemberLevel()) }>{ LocalizeText('widget.generic_usable.button.use') }</Button> }
|
||||
</Flex>
|
||||
<Button fullWidth disabled={ (clubLevel > GetClubMemberLevel()) } variant="link" onClick={ event => wearFigureAtIndex(index) }>{ LocalizeText('widget.generic_usable.button.use') }</Button> }
|
||||
</div>
|
||||
</LayoutGridItem>
|
||||
)
|
||||
} } />
|
||||
} } overscan={ 5 } rows={ savedFigures } />
|
||||
);
|
||||
}
|
||||
|
@ -9,9 +9,10 @@ export const AvatarEditorFigureSetItemView: FC<{
|
||||
setType: string;
|
||||
partItem: IAvatarEditorCategoryPartItem;
|
||||
isSelected: boolean;
|
||||
width?: string;
|
||||
} & LayoutGridItemProps> = props =>
|
||||
{
|
||||
const { setType = null, partItem = null, isSelected = false, ...rest } = props;
|
||||
const { setType = null, partItem = null, isSelected = false, width = '100%', ...rest } = props;
|
||||
const [ assetUrl, setAssetUrl ] = useState<string>('');
|
||||
const { selectedColorParts = null, getFigureStringWithFace = null } = useAvatarEditor();
|
||||
|
||||
@ -45,10 +46,10 @@ export const AvatarEditorFigureSetItemView: FC<{
|
||||
if(!partItem) return null;
|
||||
|
||||
return (
|
||||
<LayoutGridItem itemImage={ (partItem.isClear ? undefined : assetUrl) } itemActive={ isSelected } style={ { width: '100%', flex: '1', backgroundPosition: (setType === AvatarFigurePartType.HEAD) ? 'center -35px' : 'center' } } { ...rest }>
|
||||
<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" /> }
|
||||
{ partItem.isClear && <AvatarEditorIcon icon="clear" /> }
|
||||
{ !partItem.isClear && partItem.partSet.isSellable && <AvatarEditorIcon icon="sellable" position="absolute" className="end-1 bottom-1" /> }
|
||||
{ !partItem.isClear && partItem.partSet.isSellable && <AvatarEditorIcon className="end-1 bottom-1 position-absolute" icon="sellable" /> }
|
||||
</LayoutGridItem>
|
||||
);
|
||||
}
|
||||
|
@ -5,10 +5,11 @@ import { useAvatarEditor } from '../../../hooks';
|
||||
import { AvatarEditorFigureSetItemView } from './AvatarEditorFigureSetItemView';
|
||||
|
||||
export const AvatarEditorFigureSetView: FC<{
|
||||
category: IAvatarEditorCategory
|
||||
category: IAvatarEditorCategory;
|
||||
columnCount: number;
|
||||
}> = props =>
|
||||
{
|
||||
const { category = null } = props;
|
||||
const { category = null, columnCount = 3 } = props;
|
||||
const { selectedParts = null, selectEditorPart } = useAvatarEditor();
|
||||
|
||||
const isPartItemSelected = (partItem: IAvatarEditorCategoryPartItem) =>
|
||||
@ -28,13 +29,13 @@ export const AvatarEditorFigureSetView: FC<{
|
||||
}
|
||||
|
||||
return (
|
||||
<InfiniteGrid rows={ category.partItems } columnCount={ 3 } overscan={ 5 } itemRender={ (item: IAvatarEditorCategoryPartItem) =>
|
||||
<InfiniteGrid columnCount={ columnCount } itemRender={ (item: IAvatarEditorCategoryPartItem) =>
|
||||
{
|
||||
if(!item) return null;
|
||||
|
||||
return (
|
||||
<AvatarEditorFigureSetItemView setType={ category.setType } partItem={ item } isSelected={ isPartItemSelected(item) } onClick={ event => selectEditorPart(category.setType, item.partSet?.id ?? -1) } />
|
||||
<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 } />
|
||||
);
|
||||
}
|
||||
|
@ -7,16 +7,17 @@ export const AvatarEditorPaletteSetItem: FC<{
|
||||
setType: string;
|
||||
partColor: IPartColor;
|
||||
isSelected: boolean;
|
||||
width?: string;
|
||||
} & LayoutGridItemProps> = props =>
|
||||
{
|
||||
const { setType = null, partColor = null, isSelected = false, ...rest } = props;
|
||||
const { setType = null, partColor = null, isSelected = false, width = '100%', ...rest } = props;
|
||||
|
||||
if(!partColor) return null;
|
||||
|
||||
const isHC = !GetConfigurationValue<boolean>('hc.disabled', false) && (partColor.clubLevel > 0);
|
||||
|
||||
return (
|
||||
<LayoutGridItem itemHighlight itemColor={ ColorConverter.int2rgb(partColor.rgb) } itemActive={ isSelected } className="clear-bg" { ...rest }>
|
||||
<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>
|
||||
);
|
||||
|
@ -6,11 +6,12 @@ import { useAvatarEditor } from '../../../hooks';
|
||||
import { AvatarEditorPaletteSetItem } from './AvatarEditorPaletteSetItemView';
|
||||
|
||||
export const AvatarEditorPaletteSetView: FC<{
|
||||
category: IAvatarEditorCategory,
|
||||
category: IAvatarEditorCategory;
|
||||
paletteIndex: number;
|
||||
columnCount: number;
|
||||
}> = props =>
|
||||
{
|
||||
const { category = null, paletteIndex = -1 } = props;
|
||||
const { category = null, paletteIndex = -1, columnCount = 3 } = props;
|
||||
const { selectedColorParts = null, selectEditorColor = null } = useAvatarEditor();
|
||||
|
||||
const isPartColorSelected = (partColor: IPartColor) =>
|
||||
@ -23,13 +24,13 @@ export const AvatarEditorPaletteSetView: FC<{
|
||||
}
|
||||
|
||||
return (
|
||||
<InfiniteGrid rows={ category.colorItems[paletteIndex] } columnCount={ 5 } overscan={ 5 } itemRender={ (item: IPartColor) =>
|
||||
<InfiniteGrid columnCount={ columnCount } itemRender={ (item: IPartColor) =>
|
||||
{
|
||||
if(!item) return null;
|
||||
|
||||
return (
|
||||
<AvatarEditorPaletteSetItem setType={ category.setType } partColor={ item } isSelected={ isPartColorSelected(item) } onClick={ event => selectEditorColor(category.setType, paletteIndex, item.id) } />
|
||||
<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] } />
|
||||
);
|
||||
}
|
||||
|
@ -88,9 +88,9 @@ export const CameraWidgetView: FC<{}> = props =>
|
||||
|
||||
return (
|
||||
<>
|
||||
{ (mode === MODE_CAPTURE) && <CameraWidgetCaptureView onClose={ () => processAction('close') } onEdit={ () => processAction('edit') } onDelete={ () => processAction('delete') } /> }
|
||||
{ (mode === MODE_EDITOR) && <CameraWidgetEditorView picture={ cameraRoll[selectedPictureIndex] } myLevel={ myLevel } onClose={ () => processAction('close') } onCancel={ () => processAction('editor_cancel') } onCheckout={ checkoutPictureUrl } availableEffects={ availableEffects } /> }
|
||||
{ (mode === MODE_CHECKOUT) && <CameraWidgetCheckoutView base64Url={ base64Url } onCloseClick={ () => processAction('close') } onCancelClick={ () => processAction('editor_cancel') } price={ price }></CameraWidgetCheckoutView> }
|
||||
{ (mode === MODE_CAPTURE) && <CameraWidgetCaptureView onClose={ () => processAction('close') } onDelete={ () => processAction('delete') } onEdit={ () => processAction('edit') } /> }
|
||||
{ (mode === MODE_EDITOR) && <CameraWidgetEditorView availableEffects={ availableEffects } myLevel={ myLevel } picture={ cameraRoll[selectedPictureIndex] } onCancel={ () => processAction('editor_cancel') } onCheckout={ checkoutPictureUrl } onClose={ () => processAction('close') } /> }
|
||||
{ (mode === MODE_CHECKOUT) && <CameraWidgetCheckoutView base64Url={ base64Url } price={ price } onCancelClick={ () => processAction('editor_cancel') } onCloseClick={ () => processAction('close') }></CameraWidgetCheckoutView> }
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import { GetRoomEngine, NitroRectangle, TextureUtils } from '@nitrots/nitro-rend
|
||||
import { FC, useRef } from 'react';
|
||||
import { FaTimes } from 'react-icons/fa';
|
||||
import { CameraPicture, GetRoomSession, LocalizeText, PlaySound, SoundNames } from '../../../api';
|
||||
import { Column, DraggableWindow, Flex } from '../../../common';
|
||||
import { Column, DraggableWindow } from '../../../common';
|
||||
import { useCamera, useNotification } from '../../../hooks';
|
||||
|
||||
export interface CameraWidgetCaptureViewProps
|
||||
@ -73,17 +73,17 @@ export const CameraWidgetCaptureView: FC<CameraWidgetCaptureViewProps> = props =
|
||||
<button className="btn btn-danger" onClick={ onDelete }>{ LocalizeText('camera.delete.button.text') }</button>
|
||||
</div>
|
||||
</div> }
|
||||
<div className="d-flex justify-content-center">
|
||||
<div className="flex justify-center">
|
||||
<div className="camera-button" title={ LocalizeText('camera.take.photo.button.tooltip') } onClick={ takePicture } />
|
||||
</div>
|
||||
</div>
|
||||
{ (cameraRoll.length > 0) &&
|
||||
<Flex gap={ 2 } justifyContent="center" className="camera-roll d-flex justify-content-center py-2">
|
||||
<div className="camera-roll flex justify-center py-2">
|
||||
{ cameraRoll.map((picture, index) =>
|
||||
{
|
||||
return <img alt="" key={ index } src={ picture.imageUrl } onClick={ event => setSelectedPictureIndex(index) } />;
|
||||
return <img key={ index } alt="" src={ picture.imageUrl } onClick={ event => setSelectedPictureIndex(index) } />;
|
||||
}) }
|
||||
</Flex> }
|
||||
</div> }
|
||||
</Column>
|
||||
</DraggableWindow>
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { CameraPublishStatusMessageEvent, CameraPurchaseOKMessageEvent, CameraStorageUrlMessageEvent, CreateLinkEvent, GetRoomEngine, PublishPhotoMessageComposer, PurchasePhotoMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect, useMemo, useState } from 'react';
|
||||
import { GetConfigurationValue, LocalizeText, SendMessageComposer } from '../../../api';
|
||||
import { Button, Column, Flex, LayoutCurrencyIcon, LayoutImage, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../common';
|
||||
import { Button, Column, LayoutCurrencyIcon, LayoutImage, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../common';
|
||||
import { useMessageEvent } from '../../../hooks';
|
||||
|
||||
export interface CameraWidgetCheckoutViewProps
|
||||
@ -85,46 +85,46 @@ export const CameraWidgetCheckoutView: FC<CameraWidgetCheckoutViewProps> = props
|
||||
<NitroCardView className="nitro-camera-checkout" theme="primary-slim">
|
||||
<NitroCardHeaderView headerText={ LocalizeText('camera.confirm_phase.title') } onCloseClick={ event => processAction('close') } />
|
||||
<NitroCardContentView>
|
||||
<Flex center>
|
||||
<div className="flex items-center justify-center">
|
||||
{ (pictureUrl && pictureUrl.length) &&
|
||||
<LayoutImage className="picture-preview border" imageUrl={ pictureUrl } /> }
|
||||
{ (!pictureUrl || !pictureUrl.length) &&
|
||||
<Flex center className="picture-preview border">
|
||||
<div className="flex items-center justify-center picture-preview border">
|
||||
<Text bold>{ LocalizeText('camera.loading') }</Text>
|
||||
</Flex> }
|
||||
</Flex>
|
||||
<Flex justifyContent="between" alignItems="center" className="bg-muted rounded p-2">
|
||||
<Column size={ publishDisabled ? 10 : 6 } gap={ 1 }>
|
||||
</div> }
|
||||
</div>
|
||||
<div className="flex items-center bg-muted rounded p-2 justify-content-between">
|
||||
<Column gap={ 1 } size={ publishDisabled ? 10 : 6 }>
|
||||
<Text bold>
|
||||
{ LocalizeText('camera.purchase.header') }
|
||||
</Text>
|
||||
{ ((price.credits > 0) || (price.duckets > 0)) &&
|
||||
<Flex gap={ 1 }>
|
||||
<div className="flex gap-1">
|
||||
<Text>{ LocalizeText('catalog.purchase.confirmation.dialog.cost') }</Text>
|
||||
{ (price.credits > 0) &&
|
||||
<Flex gap={ 1 }>
|
||||
<div className="flex gap-1">
|
||||
<Text bold>{ price.credits }</Text>
|
||||
<LayoutCurrencyIcon type={ -1 } />
|
||||
</Flex> }
|
||||
</div> }
|
||||
{ (price.duckets > 0) &&
|
||||
<Flex gap={ 1 }>
|
||||
<div className="flex gap-1">
|
||||
<Text bold>{ price.duckets }</Text>
|
||||
<LayoutCurrencyIcon type={ 5 } />
|
||||
</Flex> }
|
||||
</Flex> }
|
||||
</div> }
|
||||
</div> }
|
||||
{ (picturesBought > 0) &&
|
||||
<Text>
|
||||
<Text bold>{ LocalizeText('camera.purchase.count.info') }</Text> { picturesBought }
|
||||
<u className="ms-1 cursor-pointer" onClick={ () => CreateLinkEvent('inventory/toggle') }>{ LocalizeText('camera.open.inventory') }</u>
|
||||
</Text> }
|
||||
</Column>
|
||||
<Flex alignItems="center">
|
||||
<Button variant="success" disabled={ isWaiting } onClick={ event => processAction('buy') }>{ LocalizeText(!picturesBought ? 'buy' : 'camera.buy.another.button.text') }</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
<div className="flex items-center">
|
||||
<Button disabled={ isWaiting } variant="success" onClick={ event => processAction('buy') }>{ LocalizeText(!picturesBought ? 'buy' : 'camera.buy.another.button.text') }</Button>
|
||||
</div>
|
||||
</div>
|
||||
{ !publishDisabled &&
|
||||
<Flex justifyContent="between" alignItems="center" className="bg-muted rounded p-2">
|
||||
<Column gap={ 1 }>
|
||||
<div className="flex items-center justify-content-between bg-muted rounded p-2">
|
||||
<div className="flex flex-column gap-1">
|
||||
<Text bold>
|
||||
{ LocalizeText(wasPicturePublished ? 'camera.publish.successful' : 'camera.publish.explanation') }
|
||||
</Text>
|
||||
@ -133,26 +133,26 @@ export const CameraWidgetCheckoutView: FC<CameraWidgetCheckoutViewProps> = props
|
||||
</Text>
|
||||
{ wasPicturePublished && <a href={ publishUrl } rel="noreferrer" target="_blank">{ LocalizeText('camera.link.to.published') }</a> }
|
||||
{ !wasPicturePublished && (price.publishDucketPrice > 0) &&
|
||||
<Flex gap={ 1 }>
|
||||
<div className="flex gap-1">
|
||||
<Text>{ LocalizeText('catalog.purchase.confirmation.dialog.cost') }</Text>
|
||||
<Flex gap={ 1 }>
|
||||
<div className="flex gap-1">
|
||||
<Text bold>{ price.publishDucketPrice }</Text>
|
||||
<LayoutCurrencyIcon type={ 5 } />
|
||||
</Flex>
|
||||
</Flex> }
|
||||
</div>
|
||||
</div> }
|
||||
{ (publishCooldown > 0) && <div className="mt-1 text-center fw-bold">{ LocalizeText('camera.publish.wait', [ 'minutes' ], [ Math.ceil( publishCooldown / 60).toString() ]) }</div> }
|
||||
</Column>
|
||||
</div>
|
||||
{ !wasPicturePublished &&
|
||||
<Flex className="d-flex align-items-end">
|
||||
<Button variant="success" disabled={ (isWaiting || (publishCooldown > 0)) } onClick={ event => processAction('publish') }>
|
||||
<div className="flex align-items-end">
|
||||
<Button disabled={ (isWaiting || (publishCooldown > 0)) } variant="success" onClick={ event => processAction('publish') }>
|
||||
{ LocalizeText('camera.publish.button.text') }
|
||||
</Button>
|
||||
</Flex> }
|
||||
</Flex> }
|
||||
</div> }
|
||||
</div> }
|
||||
<Text center>{ LocalizeText('camera.warning.disclaimer') }</Text>
|
||||
<Flex justifyContent="end">
|
||||
<div className="flex justify-content-end">
|
||||
<Button onClick={ event => processAction('cancel') }>{ LocalizeText('generic.cancel') }</Button>
|
||||
</Flex>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
|
@ -55,10 +55,10 @@ export const CameraWidgetShowPhotoView: FC<CameraWidgetShowPhotoViewProps> = pro
|
||||
</Flex>
|
||||
{ currentImage.m && currentImage.m.length &&
|
||||
<Text center>{ currentImage.m }</Text> }
|
||||
<Flex alignItems="center" justifyContent="between">
|
||||
<div className="flex items-center justify-content-between">
|
||||
<Text>{ (currentImage.n || '') }</Text>
|
||||
<Text>{ new Date(currentImage.t * 1000).toLocaleDateString() }</Text>
|
||||
</Flex>
|
||||
</div>
|
||||
{ (currentPhotos.length > 1) &&
|
||||
<Flex className="picture-preview-buttons">
|
||||
<FaArrowLeft className="cursor-pointer picture-preview-buttons-previous fa-icon" onClick={ previous } />
|
||||
|
@ -3,7 +3,7 @@ import { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { FaSave, FaSearchMinus, FaSearchPlus, FaTrash } from 'react-icons/fa';
|
||||
import ReactSlider from 'react-slider';
|
||||
import { CameraEditorTabs, CameraPicture, CameraPictureThumbnail, LocalizeText } from '../../../../api';
|
||||
import { Button, ButtonGroup, Column, Flex, Grid, LayoutImage, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView, Text } from '../../../../common';
|
||||
import { Button, Column, Grid, LayoutImage, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView, Text } from '../../../../common';
|
||||
import { CameraWidgetEffectListView } from './effect-list';
|
||||
|
||||
export interface CameraWidgetEditorViewProps
|
||||
@ -194,27 +194,27 @@ export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
|
||||
</NitroCardTabsView>
|
||||
<NitroCardContentView>
|
||||
<Grid>
|
||||
<Column size={ 5 } overflow="hidden">
|
||||
<CameraWidgetEffectListView myLevel={ myLevel } selectedEffects={ selectedEffects } effects={ getEffectList() } thumbnails={ effectsThumbnails } processAction={ processAction } />
|
||||
<Column overflow="hidden" size={ 5 }>
|
||||
<CameraWidgetEffectListView effects={ getEffectList() } myLevel={ myLevel } processAction={ processAction } selectedEffects={ selectedEffects } thumbnails={ effectsThumbnails } />
|
||||
</Column>
|
||||
<Column size={ 7 } justifyContent="between" overflow="hidden">
|
||||
<Column justifyContent="between" overflow="hidden" size={ 7 }>
|
||||
<Column center>
|
||||
<LayoutImage imageUrl={ currentPictureUrl } className="picture-preview" />
|
||||
<LayoutImage className="picture-preview" imageUrl={ currentPictureUrl } />
|
||||
{ selectedEffectName &&
|
||||
<Column center fullWidth gap={ 1 }>
|
||||
<Text>{ LocalizeText('camera.effect.name.' + selectedEffectName) }</Text>
|
||||
<ReactSlider
|
||||
className={ 'nitro-slider' }
|
||||
min={ 0 }
|
||||
max={ 1 }
|
||||
min={ 0 }
|
||||
renderThumb={ (props, state) => <div { ...props }>{ state.valueNow }</div> }
|
||||
step={ 0.01 }
|
||||
value={ getCurrentEffect.alpha }
|
||||
onChange={ event => setSelectedEffectAlpha(event) }
|
||||
renderThumb={ (props, state) => <div { ...props }>{ state.valueNow }</div> } />
|
||||
onChange={ event => setSelectedEffectAlpha(event) } />
|
||||
</Column> }
|
||||
</Column>
|
||||
<Flex justifyContent="between">
|
||||
<ButtonGroup>
|
||||
<div className="flex justify-content-between">
|
||||
<div className="btn-group">
|
||||
<Button onClick={ event => processAction('clear_effects') }>
|
||||
<FaTrash className="fa-icon" />
|
||||
</Button>
|
||||
@ -225,16 +225,16 @@ export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
|
||||
{ isZoomed && <FaSearchMinus className="fa-icon" /> }
|
||||
{ !isZoomed && <FaSearchPlus className="fa-icon" /> }
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
<Flex gap={ 1 }>
|
||||
</div>
|
||||
<div className="flex gap-1">
|
||||
<Button onClick={ event => processAction('cancel') }>
|
||||
{ LocalizeText('generic.cancel') }
|
||||
</Button>
|
||||
<Button onClick={ event => processAction('checkout') }>
|
||||
{ LocalizeText('camera.preview.button.text') }
|
||||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</div>
|
||||
</div>
|
||||
</Column>
|
||||
</Grid>
|
||||
</NitroCardContentView>
|
||||
|
@ -19,9 +19,9 @@ export const CameraWidgetEffectListItemView: FC<CameraWidgetEffectListItemViewPr
|
||||
const { effect = null, thumbnailUrl = null, isActive = false, isLocked = false, selectEffect = null, removeEffect = null } = props;
|
||||
|
||||
return (
|
||||
<LayoutGridItem title={ LocalizeText(!isLocked ? (`camera.effect.name.${ effect.name }`) : `camera.effect.required.level ${ effect.minLevel }`) } itemActive={ isActive } onClick={ event => (!isActive && selectEffect()) }>
|
||||
<LayoutGridItem itemActive={ isActive } title={ LocalizeText(!isLocked ? (`camera.effect.name.${ effect.name }`) : `camera.effect.required.level ${ effect.minLevel }`) } onClick={ event => (!isActive && selectEffect()) }>
|
||||
{ isActive &&
|
||||
<Button variant="danger" className="rounded-circle remove-effect" onClick={ removeEffect }>
|
||||
<Button className="rounded-circle remove-effect" variant="danger" onClick={ removeEffect }>
|
||||
<FaTimes className="fa-icon" />
|
||||
</Button> }
|
||||
{ !isLocked && (thumbnailUrl && thumbnailUrl.length > 0) &&
|
||||
@ -29,7 +29,7 @@ export const CameraWidgetEffectListItemView: FC<CameraWidgetEffectListItemViewPr
|
||||
<img alt="" src={ thumbnailUrl } />
|
||||
</div> }
|
||||
{ isLocked &&
|
||||
<Text center bold>
|
||||
<Text bold center>
|
||||
<div>
|
||||
<FaLock className="fa-icon" />
|
||||
</div>
|
||||
|
@ -24,7 +24,7 @@ export const CameraWidgetEffectListView: FC<CameraWidgetEffectListViewProps> = p
|
||||
const thumbnailUrl = (thumbnails.find(thumbnail => (thumbnail.effectName === effect.name)));
|
||||
const isActive = (selectedEffects.findIndex(selectedEffect => (selectedEffect.effect.name === effect.name)) > -1);
|
||||
|
||||
return <CameraWidgetEffectListItemView key={ index } effect={ effect } thumbnailUrl={ ((thumbnailUrl && thumbnailUrl.thumbnailUrl) || null) } isActive={ isActive } isLocked={ (effect.minLevel > myLevel) } selectEffect={ () => processAction('select_effect', effect.name) } removeEffect={ () => processAction('remove_effect', effect.name) } />
|
||||
return <CameraWidgetEffectListItemView key={ index } effect={ effect } isActive={ isActive } isLocked={ (effect.minLevel > myLevel) } removeEffect={ () => processAction('remove_effect', effect.name) } selectEffect={ () => processAction('select_effect', effect.name) } thumbnailUrl={ ((thumbnailUrl && thumbnailUrl.thumbnailUrl) || null) } />
|
||||
}) }
|
||||
</Grid>
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { GetRoomEngine, GetSessionDataManager } from '@nitrots/nitro-renderer';
|
||||
import { FC } from 'react';
|
||||
import { CalendarItemState, GetConfigurationValue, ICalendarItem } from '../../api';
|
||||
import { Base, Column, Flex, LayoutImage } from '../../common';
|
||||
import { Column, Flex, LayoutImage } from '../../common';
|
||||
|
||||
interface CalendarItemViewProps
|
||||
{
|
||||
@ -33,7 +33,7 @@ export const CalendarItemView: FC<CalendarItemViewProps> = props =>
|
||||
}
|
||||
|
||||
return (
|
||||
<Column fit center pointer className={ `campaign-spritesheet campaign-day-generic-bg rounded calendar-item ${ active ? 'active' : '' }` } onClick={ () => onClick(itemId) }>
|
||||
<Column center fit pointer className={ `campaign-spritesheet campaign-day-generic-bg rounded calendar-item ${ active ? 'active' : '' }` } onClick={ () => onClick(itemId) }>
|
||||
{ (state === CalendarItemState.STATE_UNLOCKED) &&
|
||||
<Flex center className="campaign-spritesheet unlocked-bg">
|
||||
<Flex center className="campaign-spritesheet campaign-opened">
|
||||
@ -44,9 +44,9 @@ export const CalendarItemView: FC<CalendarItemViewProps> = props =>
|
||||
{ (state !== CalendarItemState.STATE_UNLOCKED) &&
|
||||
<Flex center className="campaign-spritesheet locked-bg">
|
||||
{ (state === CalendarItemState.STATE_LOCKED_AVAILABLE) &&
|
||||
<Base className="campaign-spritesheet available" /> }
|
||||
<div className="campaign-spritesheet available" /> }
|
||||
{ ((state === CalendarItemState.STATE_LOCKED_EXPIRED) || (state === CalendarItemState.STATE_LOCKED_FUTURE)) &&
|
||||
<Base className="campaign-spritesheet unavailable" /> }
|
||||
<div className="campaign-spritesheet unavailable" /> }
|
||||
</Flex> }
|
||||
</Column>
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { GetSessionDataManager } from '@nitrots/nitro-renderer';
|
||||
import { FC, useState } from 'react';
|
||||
import { CalendarItemState, ICalendarItem, LocalizeText } from '../../api';
|
||||
import { Base, Button, Column, Flex, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../common';
|
||||
import { Button, Column, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../common';
|
||||
import { CalendarItemView } from './CalendarItemView';
|
||||
|
||||
interface CalendarViewProps
|
||||
@ -100,26 +100,26 @@ export const CalendarView: FC<CalendarViewProps> = props =>
|
||||
<NitroCardView className="nitro-campaign-calendar" theme="primary-slim">
|
||||
<NitroCardHeaderView headerText={ LocalizeText(`campaign.calendar.${ campaignName }.title`) } onCloseClick={ onClose } />
|
||||
<NitroCardContentView>
|
||||
<Grid fullHeight={ false } justifyContent="between" alignItems="center">
|
||||
<Grid alignItems="center" fullHeight={ false } justifyContent="between">
|
||||
<Column size={ 1 } />
|
||||
<Column size={ 10 }>
|
||||
<Flex justifyContent="between" alignItems="center" gap={ 1 }>
|
||||
<Column gap={ 1 }>
|
||||
<div className="flex items-center gap-1 justify-content-between">
|
||||
<div className="flex flex-column gap-1">
|
||||
<Text fontSize={ 3 }>{ LocalizeText('campaign.calendar.heading.day', [ 'number' ], [ (selectedDay + 1).toString() ]) }</Text>
|
||||
<Text>{ dayMessage(selectedDay) }</Text>
|
||||
</Column>
|
||||
</div>
|
||||
<div>
|
||||
{ GetSessionDataManager().isModerator &&
|
||||
<Button variant="danger" onClick={ forceOpen }>Force open</Button> }
|
||||
</div>
|
||||
</Flex>
|
||||
</div>
|
||||
</Column>
|
||||
<Column size={ 1 } />
|
||||
</Grid>
|
||||
<Flex fullHeight gap={ 2 }>
|
||||
<Flex center>
|
||||
<Base pointer className="campaign-spritesheet prev" onClick={ onClickPrev } />
|
||||
</Flex>
|
||||
<div className="flex h-100 gap-2">
|
||||
<div className="flex items-center justify-center">
|
||||
<div className="campaign-spritesheet prev cursor-pointer" onClick={ onClickPrev } />
|
||||
</div>
|
||||
<Column center fullWidth>
|
||||
<Grid fit columnCount={ TOTAL_SHOWN_ITEMS } gap={ 1 }>
|
||||
{ [ ...Array(TOTAL_SHOWN_ITEMS) ].map((e, i) =>
|
||||
@ -128,16 +128,16 @@ export const CalendarView: FC<CalendarViewProps> = props =>
|
||||
|
||||
return (
|
||||
<Column key={ i } overflow="hidden">
|
||||
<CalendarItemView itemId={ day } state={ getDayState(day) } active={ (selectedDay === day) } product={ receivedProducts.has(day) ? receivedProducts.get(day) : null } onClick={ onClickItem } />
|
||||
<CalendarItemView active={ (selectedDay === day) } itemId={ day } product={ receivedProducts.has(day) ? receivedProducts.get(day) : null } state={ getDayState(day) } onClick={ onClickItem } />
|
||||
</Column>
|
||||
);
|
||||
}) }
|
||||
</Grid>
|
||||
</Column>
|
||||
<Flex center>
|
||||
<Base pointer className="campaign-spritesheet next" onClick={ onClickNext } />
|
||||
</Flex>
|
||||
</Flex>
|
||||
<div className="flex items-center justify-center">
|
||||
<div className="campaign-spritesheet next cursor-pointer" onClick={ onClickNext } />
|
||||
</div>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
)
|
||||
|
@ -94,7 +94,7 @@ export const CampaignView: FC<{}> = props =>
|
||||
return (
|
||||
<>
|
||||
{ (calendarData && isCalendarOpen) &&
|
||||
<CalendarView onClose={ () => setCalendarOpen(false) } campaignName={ calendarData.campaignName } currentDay={ calendarData.currentDay } numDays={ calendarData.campaignDays } openedDays={ calendarData.openedDays } missedDays={ calendarData.missedDays } openPackage={ openPackage } receivedProducts={ receivedProducts } />
|
||||
<CalendarView campaignName={ calendarData.campaignName } currentDay={ calendarData.currentDay } missedDays={ calendarData.missedDays } numDays={ calendarData.campaignDays } openedDays={ calendarData.openedDays } openPackage={ openPackage } receivedProducts={ receivedProducts } onClose={ () => setCalendarOpen(false) } />
|
||||
}
|
||||
</>
|
||||
)
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { AddLinkEventTracker, ILinkEventTracker, RemoveLinkEventTracker } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect } from 'react';
|
||||
import { GetConfigurationValue, LocalizeText } from '../../api';
|
||||
import { Column, Flex, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../common';
|
||||
import { Column, Grid, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../common';
|
||||
import { useCatalog } from '../../hooks';
|
||||
import { CatalogIconView } from './views/catalog-icon/CatalogIconView';
|
||||
import { CatalogGiftView } from './views/gift/CatalogGiftView';
|
||||
@ -69,7 +69,7 @@ export const CatalogView: FC<{}> = props =>
|
||||
return (
|
||||
<>
|
||||
{ isVisible &&
|
||||
<NitroCardView uniqueKey="catalog" className="nitro-catalog" style={ GetConfigurationValue('catalog.headers') ? { width: 710 } : {} }>
|
||||
<NitroCardView className="nitro-catalog" style={ GetConfigurationValue('catalog.headers') ? { width: 710 } : {} } uniqueKey="catalog">
|
||||
<NitroCardHeaderView headerText={ LocalizeText('catalog.title') } onCloseClick={ event => setIsVisible(false) } />
|
||||
<NitroCardTabsView>
|
||||
{ rootNode && (rootNode.children.length > 0) && rootNode.children.map(child =>
|
||||
@ -83,10 +83,10 @@ export const CatalogView: FC<{}> = props =>
|
||||
|
||||
activateNode(child);
|
||||
} } >
|
||||
<Flex gap={ GetConfigurationValue('catalog.tab.icons') ? 1 : 0 } alignItems="center">
|
||||
<div className={ `flex items-center gap-${ GetConfigurationValue('catalog.tab.icons') ? 1 : 0 }` }>
|
||||
{ GetConfigurationValue('catalog.tab.icons') && <CatalogIconView icon={ child.iconId } /> }
|
||||
{ child.localization }
|
||||
</Flex>
|
||||
</div>
|
||||
</NitroCardTabsItemView>
|
||||
);
|
||||
}) }
|
||||
@ -94,11 +94,11 @@ export const CatalogView: FC<{}> = props =>
|
||||
<NitroCardContentView>
|
||||
<Grid>
|
||||
{ !navigationHidden &&
|
||||
<Column size={ 3 } overflow="hidden">
|
||||
<Column overflow="hidden" size={ 3 }>
|
||||
{ activeNodes && (activeNodes.length > 0) &&
|
||||
<CatalogNavigationView node={ activeNodes[0] } /> }
|
||||
</Column> }
|
||||
<Column size={ !navigationHidden ? 9 : 12 } overflow="hidden">
|
||||
<Column overflow="hidden" size={ !navigationHidden ? 9 : 12 }>
|
||||
{ GetCatalogLayout(currentPage, () => setNavigationHidden(true)) }
|
||||
</Column>
|
||||
</Grid>
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { GetConfigurationValue } from '../../../../api';
|
||||
import { Flex } from '../../../../common';
|
||||
|
||||
export interface CatalogHeaderViewProps
|
||||
{
|
||||
@ -17,10 +16,10 @@ export const CatalogHeaderView: FC<CatalogHeaderViewProps> = props =>
|
||||
setDisplayImageUrl(imageUrl ?? GetConfigurationValue<string>('catalog.asset.image.url').replace('%name%', 'catalog_header_roombuilder'));
|
||||
}, [ imageUrl ]);
|
||||
|
||||
return <Flex center fullWidth className="nitro-catalog-header">
|
||||
return <div className="flex justify-center items-center w-100 nitro-catalog-header">
|
||||
<img src={ displayImageUrl } onError={ ({ currentTarget }) =>
|
||||
{
|
||||
currentTarget.src = GetConfigurationValue<string>('catalog.asset.image.url').replace('%name%', 'catalog_header_roombuilder');
|
||||
} } />
|
||||
</Flex>;
|
||||
</div>;
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ 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 { Base, Button, ButtonGroup, 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, classNames } from '../../../../common';
|
||||
import { CatalogEvent, CatalogInitGiftEvent, CatalogPurchasedEvent } from '../../../../events';
|
||||
import { useCatalog, useFriends, useMessageEvent, useUiEvent } from '../../../../hooks';
|
||||
|
||||
@ -210,79 +210,79 @@ export const CatalogGiftView: FC<{}> = props =>
|
||||
const priceText = 'catalog.gift_wrapping_new.' + (isBoxDefault ? 'freeprice' : 'price');
|
||||
|
||||
return (
|
||||
<NitroCardView uniqueKey="catalog-gift" className="nitro-catalog-gift" theme="primary-slim">
|
||||
<NitroCardView className="nitro-catalog-gift" theme="primary-slim" uniqueKey="catalog-gift">
|
||||
<NitroCardHeaderView headerText={ LocalizeText('catalog.gift_wrapping.title') } onCloseClick={ onClose } />
|
||||
<NitroCardContentView className="text-black">
|
||||
<FormGroup column>
|
||||
<Text>{ LocalizeText('catalog.gift_wrapping.receiver') }</Text>
|
||||
<input type="text" className={ classNames('form-control form-control-sm', receiverNotFound && 'is-invalid') } value={ receiverName } onChange={ (e) => onTextChanged(e) } />
|
||||
<input className={ classNames('form-control form-control-sm', receiverNotFound && 'is-invalid') } type="text" value={ receiverName } onChange={ (e) => onTextChanged(e) } />
|
||||
{ (suggestions.length > 0 && isAutocompleteVisible) &&
|
||||
<Column className="autocomplete-gift-container">
|
||||
{ suggestions.map((friend: MessengerFriend) => (
|
||||
<Base key={ friend.id } className="autocomplete-gift-item" onClick={ (e) => selectedReceiverName(friend.name) }>{ friend.name }</Base>
|
||||
<div key={ friend.id } className="autocomplete-gift-item" onClick={ (e) => selectedReceiverName(friend.name) }>{ friend.name }</div>
|
||||
)) }
|
||||
</Column>
|
||||
}
|
||||
{ receiverNotFound &&
|
||||
<Base className="invalid-feedback">{ LocalizeText('catalog.gift_wrapping.receiver_not_found.title') }</Base> }
|
||||
<div className="invalid-feedback">{ LocalizeText('catalog.gift_wrapping.receiver_not_found.title') }</div> }
|
||||
</FormGroup>
|
||||
<LayoutGiftTagView figure={ GetSessionDataManager().figure } userName={ GetSessionDataManager().userName } message={ message } editable={ true } onChange={ (value) => setMessage(value) } />
|
||||
<Base className="form-check">
|
||||
<input className="form-check-input" type="checkbox" name="showMyFace" checked={ showMyFace } onChange={ (e) => setShowMyFace(value => !value) } />
|
||||
<LayoutGiftTagView editable={ true } figure={ GetSessionDataManager().figure } message={ message } userName={ GetSessionDataManager().userName } onChange={ (value) => setMessage(value) } />
|
||||
<div className="form-check">
|
||||
<input checked={ showMyFace } className="form-check-input" name="showMyFace" type="checkbox" onChange={ (e) => setShowMyFace(value => !value) } />
|
||||
<label className="form-check-label">{ LocalizeText('catalog.gift_wrapping.show_face.title') }</label>
|
||||
</Base>
|
||||
<Flex alignItems="center" gap={ 2 }>
|
||||
</div>
|
||||
<div className="items-center gap-2">
|
||||
{ selectedColorId &&
|
||||
<Base className="gift-preview">
|
||||
<LayoutFurniImageView productType={ ProductTypeEnum.FLOOR } productClassId={ colourId } extraData={ boxExtraData } />
|
||||
</Base> }
|
||||
<Column gap={ 1 }>
|
||||
<Flex gap={ 2 }>
|
||||
<ButtonGroup>
|
||||
<div className="gift-preview">
|
||||
<LayoutFurniImageView extraData={ boxExtraData } productClassId={ colourId } productType={ ProductTypeEnum.FLOOR } />
|
||||
</div> }
|
||||
<div className="flex flex-column gap-1">
|
||||
<div className="flex gap-2">
|
||||
<div className="btn-group">
|
||||
<Button variant="primary" onClick={ () => handleAction('prev_box') }>
|
||||
<FaChevronLeft className="fa-icon" />
|
||||
</Button>
|
||||
<Button variant="primary" onClick={ () => handleAction('next_box') }>
|
||||
<FaChevronRight className="fa-icon" />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
<Column gap={ 1 }>
|
||||
</div>
|
||||
<div className="flex flex-column gap-1">
|
||||
<Text fontWeight="bold">{ LocalizeText(boxName) }</Text>
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
<div className="flex items-center gap-1">
|
||||
{ LocalizeText(priceText, [ 'price' ], [ giftConfiguration.price.toString() ]) }
|
||||
<LayoutCurrencyIcon type={ -1 } />
|
||||
</Flex>
|
||||
</Column>
|
||||
</Flex>
|
||||
<Flex alignItems="center" gap={ 2 } className={ isColorable ? '' : 'opacity-50 pointer-events-none' }>
|
||||
<ButtonGroup>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Flex alignItems="center" className={ isColorable ? '' : 'opacity-50 pointer-events-none' } gap={ 2 }>
|
||||
<div className="btn-group">
|
||||
<Button variant="primary" onClick={ () => handleAction('prev_ribbon') }>
|
||||
<FaChevronLeft className="fa-icon" />
|
||||
</Button>
|
||||
<Button variant="primary" onClick={ () => handleAction('next_ribbon') }>
|
||||
<FaChevronRight className="fa-icon" />
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
<Text fontWeight="bold">{ LocalizeText(ribbonName) }</Text>
|
||||
</Flex>
|
||||
</Column>
|
||||
</Flex>
|
||||
<Column gap={ 1 } className={ isColorable ? '' : 'opacity-50 pointer-events-none' }>
|
||||
</div>
|
||||
</div>
|
||||
<Column className={ isColorable ? '' : 'opacity-50 pointer-events-none' } gap={ 1 }>
|
||||
<Text fontWeight="bold">
|
||||
{ LocalizeText('catalog.gift_wrapping.pick_color') }
|
||||
</Text>
|
||||
<ButtonGroup fullWidth>
|
||||
{ colors.map(color => <Button key={ color.id } variant="dark" active={ (color.id === selectedColorId) } disabled={ !isColorable } style={ { backgroundColor: color.color } } onClick={ () => setSelectedColorId(color.id) } />) }
|
||||
</ButtonGroup>
|
||||
<div className="btn-group w-100">
|
||||
{ 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>
|
||||
<Flex justifyContent="between" alignItems="center">
|
||||
<Button variant="link" onClick={ onClose } className="text-black">
|
||||
<div className="flex justify-content-between items-center">
|
||||
<Button className="text-black" variant="link" onClick={ onClose }>
|
||||
{ LocalizeText('cancel') }
|
||||
</Button>
|
||||
<Button variant="success" onClick={ () => handleAction('buy') }>
|
||||
{ LocalizeText('catalog.gift_wrapping.give_gift') }
|
||||
</Button>
|
||||
</Flex>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { FC } from 'react';
|
||||
import { FaCaretDown, FaCaretUp } from 'react-icons/fa';
|
||||
import { ICatalogNode } from '../../../../api';
|
||||
import { Base, LayoutGridItem, Text } from '../../../../common';
|
||||
import { LayoutGridItem, Text } from '../../../../common';
|
||||
import { useCatalog } from '../../../../hooks';
|
||||
import { CatalogIconView } from '../catalog-icon/CatalogIconView';
|
||||
import { CatalogNavigationSetView } from './CatalogNavigationSetView';
|
||||
@ -18,10 +18,10 @@ export const CatalogNavigationItemView: FC<CatalogNavigationItemViewProps> = pro
|
||||
const { activateNode = null } = useCatalog();
|
||||
|
||||
return (
|
||||
<Base className="nitro-catalog-navigation-section">
|
||||
<LayoutGridItem gap={ 1 } column={ false } itemActive={ node.isActive } onClick={ event => activateNode(node) } className={ child ? 'inset' : '' }>
|
||||
<div className="nitro-catalog-navigation-section">
|
||||
<LayoutGridItem className={ child ? 'inset' : '' } column={ false } gap={ 1 } itemActive={ node.isActive } onClick={ event => activateNode(node) }>
|
||||
<CatalogIconView icon={ node.iconId } />
|
||||
<Text grow truncate>{ node.localization }</Text>
|
||||
<Text truncate className="flex-grow-1">{ node.localization }</Text>
|
||||
{ node.isBranch &&
|
||||
<>
|
||||
{ node.isOpen && <FaCaretUp className="fa-icon" /> }
|
||||
@ -29,7 +29,7 @@ export const CatalogNavigationItemView: FC<CatalogNavigationItemViewProps> = pro
|
||||
</> }
|
||||
</LayoutGridItem>
|
||||
{ node.isOpen && node.isBranch &&
|
||||
<CatalogNavigationSetView node={ node } child={ true } /> }
|
||||
</Base>
|
||||
<CatalogNavigationSetView child={ true } node={ node } /> }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ export const CatalogNavigationSetView: FC<CatalogNavigationSetViewProps> = props
|
||||
{
|
||||
if(!n.isVisible) return null;
|
||||
|
||||
return <CatalogNavigationItemView key={ index } node={ n } child={ child } />
|
||||
return <CatalogNavigationItemView key={ index } child={ child } node={ n } />
|
||||
}) }
|
||||
</>
|
||||
);
|
||||
|
@ -20,7 +20,7 @@ export const CatalogNavigationView: FC<CatalogNavigationViewProps> = props =>
|
||||
<>
|
||||
<CatalogSearchView />
|
||||
<Column fullHeight className="nitro-catalog-navigation-grid-container rounded p-1" overflow="hidden">
|
||||
<AutoGrid id="nitro-catalog-main-navigation" gap={ 1 } columnCount={ 1 }>
|
||||
<AutoGrid columnCount={ 1 } gap={ 1 } id="nitro-catalog-main-navigation">
|
||||
{ searchResult && (searchResult.filteredNodes.length > 0) && searchResult.filteredNodes.map((n, index) =>
|
||||
{
|
||||
return <CatalogNavigationItemView key={ index } node={ n } />;
|
||||
|
@ -51,9 +51,9 @@ export const CatalogGridOfferView: FC<CatalogGridOfferViewProps> = props =>
|
||||
if(!product) return null;
|
||||
|
||||
return (
|
||||
<LayoutGridItem itemImage={ iconUrl } itemCount={ ((offer.pricingModel === Offer.PRICING_MODEL_MULTI) ? product.productCount : 1) } itemUniqueSoldout={ (product.uniqueLimitedItemSeriesSize && !product.uniqueLimitedItemsLeft) } itemUniqueNumber={ product.uniqueLimitedItemSeriesSize } itemActive={ itemActive } onMouseDown={ onMouseEvent } onMouseUp={ onMouseEvent } onMouseOut={ onMouseEvent } { ...rest }>
|
||||
<LayoutGridItem itemActive={ itemActive } itemCount={ ((offer.pricingModel === Offer.PRICING_MODEL_MULTI) ? product.productCount : 1) } itemImage={ iconUrl } itemUniqueNumber={ product.uniqueLimitedItemSeriesSize } itemUniqueSoldout={ (product.uniqueLimitedItemSeriesSize && !product.uniqueLimitedItemsLeft) } onMouseDown={ onMouseEvent } onMouseOut={ onMouseEvent } onMouseUp={ onMouseEvent } { ...rest }>
|
||||
{ (offer.product.productType === ProductTypeEnum.ROBOT) &&
|
||||
<LayoutAvatarImageView figure={ offer.product.extraParam } headOnly={ true } direction={ 3 } /> }
|
||||
<LayoutAvatarImageView direction={ 3 } figure={ offer.product.extraParam } headOnly={ true } /> }
|
||||
</LayoutGridItem>
|
||||
);
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import { RedeemVoucherMessageComposer, VoucherRedeemErrorMessageEvent, VoucherRe
|
||||
import { FC, useState } from 'react';
|
||||
import { FaTag } from 'react-icons/fa';
|
||||
import { LocalizeText, SendMessageComposer } from '../../../../../api';
|
||||
import { Button, Flex } from '../../../../../common';
|
||||
import { Button } from '../../../../../common';
|
||||
import { useMessageEvent, useNotification } from '../../../../../hooks';
|
||||
|
||||
export interface CatalogRedeemVoucherViewProps
|
||||
@ -50,11 +50,11 @@ export const CatalogRedeemVoucherView: FC<CatalogRedeemVoucherViewProps> = props
|
||||
});
|
||||
|
||||
return (
|
||||
<Flex gap={ 1 }>
|
||||
<input type="text" className="form-control form-control-sm" placeholder={ text } value={ voucher } onChange={ event => setVoucher(event.target.value) } />
|
||||
<Button variant="primary" onClick={ redeemVoucher } disabled={ isWaiting }>
|
||||
<div className="flex gap-1">
|
||||
<input className="form-control form-control-sm" placeholder={ text } type="text" value={ voucher } onChange={ event => setVoucher(event.target.value) } />
|
||||
<Button disabled={ isWaiting } variant="primary" onClick={ redeemVoucher }>
|
||||
<FaTag className="fa-icon" />
|
||||
</Button>
|
||||
</Flex>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -77,18 +77,18 @@ export const CatalogSearchView: FC<{}> = props =>
|
||||
}, [ offersToNodes, currentType, rootNode, searchValue, setCurrentPage, setSearchResult ]);
|
||||
|
||||
return (
|
||||
<Flex gap={ 1 }>
|
||||
<div className="flex gap-1">
|
||||
<Flex fullWidth alignItems="center" position="relative">
|
||||
<input type="text" className="form-control form-control-sm" placeholder={ LocalizeText('generic.search') } value={ searchValue } onChange={ event => setSearchValue(event.target.value) } />
|
||||
<input className="form-control form-control-sm" placeholder={ LocalizeText('generic.search') } type="text" value={ searchValue } onChange={ event => setSearchValue(event.target.value) } />
|
||||
</Flex>
|
||||
{ (!searchValue || !searchValue.length) &&
|
||||
<Button variant="primary" className="catalog-search-button">
|
||||
<Button className="catalog-search-button" variant="primary">
|
||||
<FaSearch className="fa-icon" />
|
||||
</Button> }
|
||||
{ searchValue && !!searchValue.length &&
|
||||
<Button variant="primary" className="catalog-search-button" onClick={ event => setSearchValue('') }>
|
||||
<Button className="catalog-search-button" variant="primary" onClick={ event => setSearchValue('') }>
|
||||
<FaTimes className="fa-icon" />
|
||||
</Button> }
|
||||
</Flex>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { FC } from 'react';
|
||||
import { LocalizeText } from '../../../../../api';
|
||||
import { Base, Column, Flex, Grid, Text } from '../../../../../common';
|
||||
import { Column, Grid, Text } from '../../../../../common';
|
||||
import { useCatalog } from '../../../../../hooks';
|
||||
import { CatalogBadgeSelectorWidgetView } from '../widgets/CatalogBadgeSelectorWidgetView';
|
||||
import { CatalogFirstProductSelectorWidgetView } from '../widgets/CatalogFirstProductSelectorWidgetView';
|
||||
@ -20,14 +20,14 @@ export const CatalogLayoutBadgeDisplayView: FC<CatalogLayoutProps> = props =>
|
||||
<>
|
||||
<CatalogFirstProductSelectorWidgetView />
|
||||
<Grid>
|
||||
<Column size={ 7 } overflow="hidden">
|
||||
<Column overflow="hidden" size={ 7 }>
|
||||
<CatalogItemGridWidgetView shrink />
|
||||
<Column gap={ 1 } overflow="hidden">
|
||||
<Text truncate shrink fontWeight="bold">{ LocalizeText('catalog_selectbadge') }</Text>
|
||||
<Text shrink truncate fontWeight="bold">{ LocalizeText('catalog_selectbadge') }</Text>
|
||||
<CatalogBadgeSelectorWidgetView />
|
||||
</Column>
|
||||
</Column>
|
||||
<Column center={ !currentOffer } size={ 5 } overflow="hidden">
|
||||
<Column center={ !currentOffer } overflow="hidden" size={ 5 }>
|
||||
{ !currentOffer &&
|
||||
<>
|
||||
{ !!page.localization.getImage(1) && <img alt="" src={ page.localization.getImage(1) } /> }
|
||||
@ -35,15 +35,15 @@ export const CatalogLayoutBadgeDisplayView: FC<CatalogLayoutProps> = props =>
|
||||
</> }
|
||||
{ currentOffer &&
|
||||
<>
|
||||
<Base position="relative" overflow="hidden">
|
||||
<div className="position-relative overflow-hidden">
|
||||
<CatalogViewProductWidgetView />
|
||||
</Base>
|
||||
<Column grow gap={ 1 }>
|
||||
<CatalogLimitedItemWidgetView fullWidth />
|
||||
<Text grow truncate>{ currentOffer.localizationName }</Text>
|
||||
<Flex justifyContent="end">
|
||||
</div>
|
||||
<Column className="flex-grow-1" gap={ 1 }>
|
||||
<CatalogLimitedItemWidgetView />
|
||||
<Text truncate className="flex-grow-1">{ currentOffer.localizationName }</Text>
|
||||
<div className="flex justify-content-end">
|
||||
<CatalogTotalPriceWidget alignItems="end" />
|
||||
</Flex>
|
||||
</div>
|
||||
<CatalogPurchaseWidgetView />
|
||||
</Column>
|
||||
</> }
|
||||
|
@ -2,7 +2,7 @@ import { ColorConverter } from '@nitrots/nitro-renderer';
|
||||
import { FC, useMemo, useState } from 'react';
|
||||
import { FaFillDrip } from 'react-icons/fa';
|
||||
import { IPurchasableOffer } from '../../../../../api';
|
||||
import { AutoGrid, Base, Button, Column, Flex, Grid, LayoutGridItem, Text } from '../../../../../common';
|
||||
import { AutoGrid, Button, Column, Grid, LayoutGridItem, Text } from '../../../../../common';
|
||||
import { useCatalog } from '../../../../../hooks';
|
||||
import { CatalogGridOfferView } from '../common/CatalogGridOfferView';
|
||||
import { CatalogAddOnBadgeWidgetView } from '../widgets/CatalogAddOnBadgeWidgetView';
|
||||
@ -132,17 +132,17 @@ export const CatalogLayoutColorGroupingView : FC<CatalogLayoutColorGroupViewProp
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
<Column size={ 7 } overflow="hidden">
|
||||
<Column overflow="hidden" size={ 7 }>
|
||||
<AutoGrid columnCount={ 5 }>
|
||||
{ (!colorsShowing || !currentOffer || !colorableItems.has(currentOffer.product.furnitureData.className)) &&
|
||||
offers.map((offer, index) => <CatalogGridOfferView key={ index } itemActive={ (currentOffer && (currentOffer.product.furnitureData.hasIndexedColor ? currentOffer.product.furnitureData.className === offer.product.furnitureData.className : currentOffer.offerId === offer.offerId)) } offer={ offer } selectOffer={ selectOffer }/>)
|
||||
}
|
||||
{ (colorsShowing && currentOffer && colorableItems.has(currentOffer.product.furnitureData.className)) &&
|
||||
colorableItems.get(currentOffer.product.furnitureData.className).map((color, index) => <LayoutGridItem itemHighlight key={ index } itemActive={ (currentOffer.product.furnitureData.colorIndex === index) } itemColor={ ColorConverter.int2rgb(color) } className="clear-bg" onClick={ event => selectColor(index, currentOffer.product.furnitureData.className) } />)
|
||||
colorableItems.get(currentOffer.product.furnitureData.className).map((color, index) => <LayoutGridItem key={ index } itemHighlight className="clear-bg" itemActive={ (currentOffer.product.furnitureData.colorIndex === index) } itemColor={ ColorConverter.int2rgb(color) } onClick={ event => selectColor(index, currentOffer.product.furnitureData.className) } />)
|
||||
}
|
||||
</AutoGrid>
|
||||
</Column>
|
||||
<Column center={ !currentOffer } size={ 5 } overflow="hidden">
|
||||
<Column center={ !currentOffer } overflow="hidden" size={ 5 }>
|
||||
{ !currentOffer &&
|
||||
<>
|
||||
{ !!page.localization.getImage(1) && <img alt="" src={ page.localization.getImage(1) } /> }
|
||||
@ -150,23 +150,23 @@ export const CatalogLayoutColorGroupingView : FC<CatalogLayoutColorGroupViewProp
|
||||
</> }
|
||||
{ currentOffer &&
|
||||
<>
|
||||
<Base position="relative" overflow="hidden">
|
||||
<div className="position-relative overflow-hidden">
|
||||
<CatalogViewProductWidgetView />
|
||||
<CatalogAddOnBadgeWidgetView position="absolute" className="bg-muted rounded bottom-1 end-1" />
|
||||
<CatalogAddOnBadgeWidgetView className="bg-muted rounded bottom-1 end-1" position="absolute" />
|
||||
{ currentOffer.product.furnitureData.hasIndexedColor &&
|
||||
<Button position="absolute" className="bottom-1 start-1" onClick={ event =>setColorsShowing(prev => !prev) }>
|
||||
<Button className="bottom-1 start-1" position="absolute" onClick={ event =>setColorsShowing(prev => !prev) }>
|
||||
<FaFillDrip className="fa-icon" />
|
||||
</Button> }
|
||||
</Base>
|
||||
<Column grow gap={ 1 }>
|
||||
<CatalogLimitedItemWidgetView fullWidth />
|
||||
<Text grow truncate>{ currentOffer.localizationName }</Text>
|
||||
<Flex justifyContent="between">
|
||||
<Column gap={ 1 }>
|
||||
</div>
|
||||
<Column className="flex-grow-1" gap={ 1 }>
|
||||
<CatalogLimitedItemWidgetView />
|
||||
<Text truncate className="flex-grow-1">{ currentOffer.localizationName }</Text>
|
||||
<div className="flex justify-content-between">
|
||||
<div className="flex flex-column gap-1">
|
||||
<CatalogSpinnerWidgetView />
|
||||
</Column>
|
||||
<CatalogTotalPriceWidget justifyContent="end" alignItems="end" />
|
||||
</Flex>
|
||||
</div>
|
||||
<CatalogTotalPriceWidget alignItems="end" justifyContent="end" />
|
||||
</div>
|
||||
<CatalogPurchaseWidgetView />
|
||||
</Column>
|
||||
</> }
|
||||
|
@ -20,12 +20,12 @@ export const CatalogLayoutDefaultView: FC<CatalogLayoutProps> = props =>
|
||||
return (
|
||||
<>
|
||||
<Grid>
|
||||
<Column size={ 7 } overflow="hidden">
|
||||
<Column overflow="hidden" size={ 7 }>
|
||||
{ GetConfigurationValue('catalog.headers') &&
|
||||
<CatalogHeaderView imageUrl={ currentPage.localization.getImage(0) }/> }
|
||||
<CatalogItemGridWidgetView />
|
||||
</Column>
|
||||
<Column center={ !currentOffer } size={ 5 } overflow="hidden">
|
||||
<Column center={ !currentOffer } overflow="hidden" size={ 5 }>
|
||||
{ !currentOffer &&
|
||||
<>
|
||||
{ !!page.localization.getImage(1) &&
|
||||
@ -43,14 +43,14 @@ export const CatalogLayoutDefaultView: FC<CatalogLayoutProps> = props =>
|
||||
{ (currentOffer.product.productType === ProductTypeEnum.BADGE) && <CatalogAddOnBadgeWidgetView className="scale-2" /> }
|
||||
</Flex>
|
||||
<Column grow gap={ 1 }>
|
||||
<CatalogLimitedItemWidgetView fullWidth />
|
||||
<CatalogLimitedItemWidgetView />
|
||||
<Text grow truncate>{ currentOffer.localizationName }</Text>
|
||||
<Flex justifyContent="between">
|
||||
<Column gap={ 1 }>
|
||||
<div className="flex justify-content-between">
|
||||
<div className="flex flex-column gap-1">
|
||||
<CatalogSpinnerWidgetView />
|
||||
</Column>
|
||||
<CatalogTotalPriceWidget justifyContent="end" alignItems="end" />
|
||||
</Flex>
|
||||
</div>
|
||||
<CatalogTotalPriceWidget alignItems="end" justifyContent="end" />
|
||||
</div>
|
||||
<CatalogPurchaseWidgetView />
|
||||
</Column>
|
||||
</> }
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { FC } from 'react';
|
||||
import { Base, Column, Flex, Grid, Text } from '../../../../../common';
|
||||
import { Column, Grid, Text } from '../../../../../common';
|
||||
import { useCatalog } from '../../../../../hooks';
|
||||
import { CatalogGuildBadgeWidgetView } from '../widgets/CatalogGuildBadgeWidgetView';
|
||||
import { CatalogGuildSelectorWidgetView } from '../widgets/CatalogGuildSelectorWidgetView';
|
||||
@ -16,10 +16,10 @@ export const CatalogLayouGuildCustomFurniView: FC<CatalogLayoutProps> = props =>
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
<Column size={ 7 } overflow="hidden">
|
||||
<Column overflow="hidden" size={ 7 }>
|
||||
<CatalogItemGridWidgetView />
|
||||
</Column>
|
||||
<Column center={ !currentOffer } size={ 5 } overflow="hidden">
|
||||
<Column center={ !currentOffer } overflow="hidden" size={ 5 }>
|
||||
{ !currentOffer &&
|
||||
<>
|
||||
{ !!page.localization.getImage(1) && <img alt="" src={ page.localization.getImage(1) } /> }
|
||||
@ -27,18 +27,18 @@ export const CatalogLayouGuildCustomFurniView: FC<CatalogLayoutProps> = props =>
|
||||
</> }
|
||||
{ currentOffer &&
|
||||
<>
|
||||
<Base position="relative" overflow="hidden">
|
||||
<div className="position-relative overflow-hidden">
|
||||
<CatalogViewProductWidgetView />
|
||||
<CatalogGuildBadgeWidgetView position="absolute" className="bottom-1 end-1" />
|
||||
</Base>
|
||||
<CatalogGuildBadgeWidgetView className="bottom-1 end-1" position="absolute" />
|
||||
</div>
|
||||
<Column grow gap={ 1 }>
|
||||
<Text truncate>{ currentOffer.localizationName }</Text>
|
||||
<Base grow>
|
||||
<div className="flex-grow-1">
|
||||
<CatalogGuildSelectorWidgetView />
|
||||
</Base>
|
||||
<Flex justifyContent="end">
|
||||
</div>
|
||||
<div className="flex justify-content-end">
|
||||
<CatalogTotalPriceWidget alignItems="end" />
|
||||
</Flex>
|
||||
</div>
|
||||
<CatalogPurchaseWidgetView />
|
||||
</Column>
|
||||
</> }
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { CatalogGroupsComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { SendMessageComposer } from '../../../../../api';
|
||||
import { Base, Column, Flex, Grid, Text } from '../../../../../common';
|
||||
import { Column, Grid, Text } from '../../../../../common';
|
||||
import { useCatalog } from '../../../../../hooks';
|
||||
import { CatalogFirstProductSelectorWidgetView } from '../widgets/CatalogFirstProductSelectorWidgetView';
|
||||
import { CatalogGuildSelectorWidgetView } from '../widgets/CatalogGuildSelectorWidgetView';
|
||||
@ -25,20 +25,20 @@ export const CatalogLayouGuildForumView: FC<CatalogLayoutProps> = props =>
|
||||
<>
|
||||
<CatalogFirstProductSelectorWidgetView />
|
||||
<Grid>
|
||||
<Column className="bg-muted rounded p-2 text-black" size={ 7 } overflow="hidden">
|
||||
<Base className="overflow-auto" dangerouslySetInnerHTML={ { __html: page.localization.getText(1) } } />
|
||||
<Column className="bg-muted rounded p-2 text-black" overflow="hidden" size={ 7 }>
|
||||
<div className="overflow-auto" dangerouslySetInnerHTML={ { __html: page.localization.getText(1) } } />
|
||||
</Column>
|
||||
<Column size={ 5 } overflow="hidden" gap={ 1 }>
|
||||
<Column gap={ 1 } overflow="hidden" size={ 5 }>
|
||||
{ !!currentOffer &&
|
||||
<>
|
||||
<Column grow gap={ 1 }>
|
||||
<Text truncate>{ currentOffer.localizationName }</Text>
|
||||
<Base grow>
|
||||
<div className="flex-grow-1">
|
||||
<CatalogGuildSelectorWidgetView />
|
||||
</Base>
|
||||
<Flex justifyContent="end">
|
||||
</div>
|
||||
<div className="flex justify-content-end">
|
||||
<CatalogTotalPriceWidget alignItems="end" />
|
||||
</Flex>
|
||||
</div>
|
||||
<CatalogPurchaseWidgetView noGiftOption={ true } />
|
||||
</Column>
|
||||
</> }
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { CreateLinkEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC } from 'react';
|
||||
import { LocalizeText } from '../../../../../api';
|
||||
import { Base } from '../../../../../common/Base';
|
||||
import { Button } from '../../../../../common/Button';
|
||||
import { Column } from '../../../../../common/Column';
|
||||
import { Grid } from '../../../../../common/Grid';
|
||||
@ -14,12 +13,12 @@ export const CatalogLayouGuildFrontpageView: FC<CatalogLayoutProps> = props =>
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
<Column size={ 7 } overflow="hidden" className="bg-muted rounded p-2 text-black">
|
||||
<Base dangerouslySetInnerHTML={ { __html: page.localization.getText(2) } } />
|
||||
<Base overflow="auto" dangerouslySetInnerHTML={ { __html: page.localization.getText(0) } } />
|
||||
<Base dangerouslySetInnerHTML={ { __html: page.localization.getText(1) } } />
|
||||
<Column className="bg-muted rounded p-2 text-black" overflow="hidden" size={ 7 }>
|
||||
<div dangerouslySetInnerHTML={ { __html: page.localization.getText(2) } } />
|
||||
<div className="overflow-auto" dangerouslySetInnerHTML={ { __html: page.localization.getText(0) } } />
|
||||
<div dangerouslySetInnerHTML={ { __html: page.localization.getText(1) } } />
|
||||
</Column>
|
||||
<Column center size={ 5 } overflow="hidden">
|
||||
<Column center overflow="hidden" size={ 5 }>
|
||||
<LayoutImage imageUrl={ page.localization.getImage(1) } />
|
||||
<Button onClick={ () => CreateLinkEvent('groups/create') }>
|
||||
{ LocalizeText('catalog.start.guild.purchase.button') }
|
||||
|
@ -6,8 +6,8 @@ export const CatalogLayoutInfoLoyaltyView: FC<CatalogLayoutProps> = props =>
|
||||
const { page = null } = props;
|
||||
|
||||
return (
|
||||
<div className="h-100 nitro-catalog-layout-info-loyalty text-black d-flex flex-row">
|
||||
<div className="overflow-auto h-100 d-flex flex-column info-loyalty-content">
|
||||
<div className="h-100 nitro-catalog-layout-info-loyalty text-black flex flex-row">
|
||||
<div className="overflow-auto h-100 flex flex-column info-loyalty-content">
|
||||
<div dangerouslySetInnerHTML={ { __html: page.localization.getText(0) } } />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { FC } from 'react';
|
||||
import { Base, Column, Flex } from '../../../../../common';
|
||||
import { Column } from '../../../../../common';
|
||||
import { CatalogLayoutProps } from './CatalogLayout.types';
|
||||
|
||||
export const CatalogLayoutPets3View: FC<CatalogLayoutProps> = props =>
|
||||
@ -10,16 +10,16 @@ export const CatalogLayoutPets3View: FC<CatalogLayoutProps> = props =>
|
||||
|
||||
return (
|
||||
<Column grow className="bg-muted rounded text-black p-2" overflow="hidden">
|
||||
<Flex alignItems="center" gap={ 2 }>
|
||||
<div className="items-center gap-2">
|
||||
{ imageUrl && <img alt="" src={ imageUrl } /> }
|
||||
<Base className="fs-5" dangerouslySetInnerHTML={ { __html: page.localization.getText(1) } } />
|
||||
</Flex>
|
||||
<div className="fs-5" dangerouslySetInnerHTML={ { __html: page.localization.getText(1) } } />
|
||||
</div>
|
||||
<Column grow alignItems="center" overflow="auto">
|
||||
<Base dangerouslySetInnerHTML={ { __html: page.localization.getText(2) } } />
|
||||
<div dangerouslySetInnerHTML={ { __html: page.localization.getText(2) } } />
|
||||
</Column>
|
||||
<Flex alignItems="center">
|
||||
<Base className="fw-bold" dangerouslySetInnerHTML={ { __html: page.localization.getText(3) } } />
|
||||
</Flex>
|
||||
<div className="flex items-center">
|
||||
<div className="fw-bold" dangerouslySetInnerHTML={ { __html: page.localization.getText(3) } } />
|
||||
</div>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { GetRoomAdPurchaseInfoComposer, GetUserEventCatsMessageComposer, PurchaseRoomAdMessageComposer, RoomAdPurchaseInfoEvent, RoomEntryData } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { LocalizeText, SendMessageComposer } from '../../../../../api';
|
||||
import { Base, Button, Column, Text } from '../../../../../common';
|
||||
import { Button, Column, Text } from '../../../../../common';
|
||||
import { useCatalog, useMessageEvent, useNavigator, useRoomPromote } from '../../../../../hooks';
|
||||
import { CatalogLayoutProps } from './CatalogLayout.types';
|
||||
|
||||
@ -73,34 +73,34 @@ export const CatalogLayoutRoomAdsView: FC<CatalogLayoutProps> = props =>
|
||||
|
||||
return (<>
|
||||
<Text bold center>{ LocalizeText('roomad.catalog_header') }</Text>
|
||||
<Column size={ 12 } overflow="hidden" className="text-black">
|
||||
<Base>{ LocalizeText('roomad.catalog_text', [ 'duration' ], [ '120' ]) }</Base>
|
||||
<Base className="bg-muted rounded p-1">
|
||||
<Column className="text-black" overflow="hidden" size={ 12 }>
|
||||
<div>{ LocalizeText('roomad.catalog_text', [ 'duration' ], [ '120' ]) }</div>
|
||||
<div className="bg-muted rounded p-1">
|
||||
<Column gap={ 2 }>
|
||||
<Text bold>{ LocalizeText('navigator.category') }</Text>
|
||||
<select className="form-select form-select-sm" value={ categoryId } onChange={ event => setCategoryId(parseInt(event.target.value)) } disabled={ extended }>
|
||||
<select className="form-select form-select-sm" disabled={ extended } value={ categoryId } onChange={ event => setCategoryId(parseInt(event.target.value)) }>
|
||||
{ categories && categories.map((cat, index) => <option key={ index } value={ cat.id }>{ LocalizeText(cat.name) }</option>) }
|
||||
</select>
|
||||
</Column>
|
||||
<Column gap={ 1 }>
|
||||
<div className="flex flex-column gap-1">
|
||||
<Text bold>{ LocalizeText('roomad.catalog_name') }</Text>
|
||||
<input type="text" className="form-control form-control-sm" maxLength={ 64 } value={ eventName } onChange={ event => setEventName(event.target.value) } readOnly={ extended } />
|
||||
</Column>
|
||||
<Column gap={ 1 }>
|
||||
<input className="form-control form-control-sm" maxLength={ 64 } readOnly={ extended } type="text" value={ eventName } onChange={ event => setEventName(event.target.value) } />
|
||||
</div>
|
||||
<div className="flex flex-column gap-1">
|
||||
<Text bold>{ LocalizeText('roomad.catalog_description') }</Text>
|
||||
<textarea className="form-control form-control-sm" maxLength={ 64 } value={ eventDesc } onChange={ event => setEventDesc(event.target.value) } readOnly={ extended } />
|
||||
</Column>
|
||||
<Column gap={ 1 }>
|
||||
<textarea className="form-control form-control-sm" maxLength={ 64 } readOnly={ extended } value={ eventDesc } onChange={ event => setEventDesc(event.target.value) } />
|
||||
</div>
|
||||
<div className="flex flex-column gap-1">
|
||||
<Text bold>{ LocalizeText('roomad.catalog_roomname') }</Text>
|
||||
<select className="form-select form-select-sm" value={ roomId } onChange={ event => setRoomId(parseInt(event.target.value)) } disabled={ extended }>
|
||||
<option value={ -1 } disabled>{ LocalizeText('roomad.catalog_roomname') }</option>
|
||||
<select className="form-select form-select-sm" disabled={ extended } value={ roomId } onChange={ event => setRoomId(parseInt(event.target.value)) }>
|
||||
<option disabled value={ -1 }>{ LocalizeText('roomad.catalog_roomname') }</option>
|
||||
{ availableRooms && availableRooms.map((room, index) => <option key={ index } value={ room.roomId }>{ room.roomName }</option>) }
|
||||
</select>
|
||||
</Column>
|
||||
<Column gap={ 1 }>
|
||||
<Button variant={ (!eventName || !eventDesc || roomId === -1) ? 'danger' : 'success' } onClick={ purchaseAd } disabled={ (!eventName || !eventDesc || roomId === -1) }>{ extended ? LocalizeText('roomad.extend.event') : LocalizeText('buy') }</Button>
|
||||
</Column>
|
||||
</Base>
|
||||
</div>
|
||||
<div className="flex flex-column gap-1">
|
||||
<Button disabled={ (!eventName || !eventDesc || roomId === -1) } variant={ (!eventName || !eventDesc || roomId === -1) ? 'danger' : 'success' } onClick={ purchaseAd }>{ extended ? LocalizeText('roomad.extend.event') : LocalizeText('buy') }</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Column>
|
||||
</>
|
||||
);
|
||||
|
@ -15,25 +15,25 @@ export const CatalogLayoutRoomBundleView: FC<CatalogLayoutProps> = props =>
|
||||
<>
|
||||
<CatalogFirstProductSelectorWidgetView />
|
||||
<Grid>
|
||||
<Column size={ 7 } overflow="hidden">
|
||||
<Column overflow="hidden" size={ 7 }>
|
||||
{ !!page.localization.getText(2) &&
|
||||
<Text dangerouslySetInnerHTML={ { __html: page.localization.getText(2) } } /> }
|
||||
<Column grow overflow="hidden" className="bg-muted p-2 rounded">
|
||||
<Column grow className="bg-muted p-2 rounded" overflow="hidden">
|
||||
<CatalogBundleGridWidgetView fullWidth className="nitro-catalog-layout-bundle-grid" />
|
||||
</Column>
|
||||
</Column>
|
||||
<Column size={ 5 } overflow="hidden" gap={ 1 }>
|
||||
<Column gap={ 1 } overflow="hidden" size={ 5 }>
|
||||
{ !!page.localization.getText(1) &&
|
||||
<Text center small overflow="auto">{ page.localization.getText(1) }</Text> }
|
||||
<Column grow position="relative" overflow="hidden" gap={ 0 }>
|
||||
<Column grow gap={ 0 } overflow="hidden" position="relative">
|
||||
{ !!page.localization.getImage(1) &&
|
||||
<img alt="" className="flex-grow-1" src={ page.localization.getImage(1) } /> }
|
||||
<CatalogAddOnBadgeWidgetView position="absolute" className="bg-muted rounded bottom-0 start-0" />
|
||||
<CatalogSimplePriceWidgetView position="absolute" className="bottom-0 end-0" />
|
||||
<CatalogAddOnBadgeWidgetView className="bg-muted rounded bottom-0 start-0" position="absolute" />
|
||||
<CatalogSimplePriceWidgetView />
|
||||
</Column>
|
||||
<Column gap={ 1 }>
|
||||
<div className="flex flex-column gap-1">
|
||||
<CatalogPurchaseWidgetView />
|
||||
</Column>
|
||||
</div>
|
||||
</Column>
|
||||
</Grid>
|
||||
</>
|
||||
|
@ -15,25 +15,25 @@ export const CatalogLayoutSingleBundleView: FC<CatalogLayoutProps> = props =>
|
||||
<>
|
||||
<CatalogFirstProductSelectorWidgetView />
|
||||
<Grid>
|
||||
<Column size={ 7 } overflow="hidden">
|
||||
<Column overflow="hidden" size={ 7 }>
|
||||
{ !!page.localization.getText(2) &&
|
||||
<Text dangerouslySetInnerHTML={ { __html: page.localization.getText(2) } } /> }
|
||||
<Column grow overflow="hidden" className="bg-muted p-2 rounded">
|
||||
<Column grow className="bg-muted p-2 rounded" overflow="hidden">
|
||||
<CatalogBundleGridWidgetView fullWidth className="nitro-catalog-layout-bundle-grid" />
|
||||
</Column>
|
||||
</Column>
|
||||
<Column size={ 5 } overflow="hidden" gap={ 1 }>
|
||||
<Column gap={ 1 } overflow="hidden" size={ 5 }>
|
||||
{ !!page.localization.getText(1) &&
|
||||
<Text center small overflow="auto">{ page.localization.getText(1) }</Text> }
|
||||
<Column grow position="relative" overflow="hidden" gap={ 0 }>
|
||||
<Column grow gap={ 0 } overflow="hidden" position="relative">
|
||||
{ !!page.localization.getImage(1) &&
|
||||
<img alt="" className="flex-grow-1" src={ page.localization.getImage(1) } /> }
|
||||
<CatalogAddOnBadgeWidgetView position="absolute" className="bg-muted rounded bottom-0 start-0" />
|
||||
<CatalogSimplePriceWidgetView position="absolute" className="bottom-0 end-0" />
|
||||
<CatalogAddOnBadgeWidgetView className="bg-muted rounded bottom-0 start-0" position="absolute" />
|
||||
<CatalogSimplePriceWidgetView />
|
||||
</Column>
|
||||
<Column gap={ 1 }>
|
||||
<div className="flex flex-column gap-1">
|
||||
<CatalogPurchaseWidgetView />
|
||||
</Column>
|
||||
</div>
|
||||
</Column>
|
||||
</Grid>
|
||||
</>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { GetOfficialSongIdMessageComposer, GetSoundManager, MusicPriorities, OfficialSongIdMessageEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { GetConfigurationValue, LocalizeText, ProductTypeEnum, SendMessageComposer } from '../../../../../api';
|
||||
import { Button, Column, Flex, Grid, LayoutImage, Text } from '../../../../../common';
|
||||
import { Button, Column, Grid, LayoutImage, Text } from '../../../../../common';
|
||||
import { useCatalog, useMessageEvent } from '../../../../../hooks';
|
||||
import { CatalogHeaderView } from '../../catalog-header/CatalogHeaderView';
|
||||
import { CatalogAddOnBadgeWidgetView } from '../widgets/CatalogAddOnBadgeWidgetView';
|
||||
@ -70,12 +70,12 @@ export const CatalogLayoutSoundMachineView: FC<CatalogLayoutProps> = props =>
|
||||
return (
|
||||
<>
|
||||
<Grid>
|
||||
<Column size={ 7 } overflow="hidden">
|
||||
<Column overflow="hidden" size={ 7 }>
|
||||
{ GetConfigurationValue('catalog.headers') &&
|
||||
<CatalogHeaderView imageUrl={ currentPage.localization.getImage(0) }/> }
|
||||
<CatalogItemGridWidgetView />
|
||||
</Column>
|
||||
<Column center={ !currentOffer } size={ 5 } overflow="hidden">
|
||||
<Column center={ !currentOffer } overflow="hidden" size={ 5 }>
|
||||
{ !currentOffer &&
|
||||
<>
|
||||
{ !!page.localization.getImage(1) &&
|
||||
@ -84,25 +84,25 @@ export const CatalogLayoutSoundMachineView: FC<CatalogLayoutProps> = props =>
|
||||
</> }
|
||||
{ currentOffer &&
|
||||
<>
|
||||
<Flex center overflow="hidden" style={ { height: 140 } }>
|
||||
<div className="flex items-center justify-center overflow-hidden" style={ { height: 140 } }>
|
||||
{ (currentOffer.product.productType !== ProductTypeEnum.BADGE) &&
|
||||
<>
|
||||
<CatalogViewProductWidgetView />
|
||||
<CatalogAddOnBadgeWidgetView className="bg-muted rounded bottom-1 end-1" />
|
||||
</> }
|
||||
{ (currentOffer.product.productType === ProductTypeEnum.BADGE) && <CatalogAddOnBadgeWidgetView className="scale-2" /> }
|
||||
</Flex>
|
||||
</div>
|
||||
<Column grow gap={ 1 }>
|
||||
<CatalogLimitedItemWidgetView fullWidth />
|
||||
<CatalogLimitedItemWidgetView />
|
||||
<Text grow truncate>{ currentOffer.localizationName }</Text>
|
||||
{ songId > -1 && <Button onClick={ () => previewSong(songId) }>{ LocalizeText('play_preview_button') }</Button>
|
||||
}
|
||||
<Flex justifyContent="between">
|
||||
<Column gap={ 1 }>
|
||||
<div className="flex justify-content-between">
|
||||
<div className="flex flex-column gap-1">
|
||||
<CatalogSpinnerWidgetView />
|
||||
</Column>
|
||||
<CatalogTotalPriceWidget justifyContent="end" alignItems="end" />
|
||||
</Flex>
|
||||
</div>
|
||||
<CatalogTotalPriceWidget alignItems="end" justifyContent="end" />
|
||||
</div>
|
||||
<CatalogPurchaseWidgetView />
|
||||
</Column>
|
||||
</> }
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { FC, useEffect } from 'react';
|
||||
import { Base, Column, Flex, Grid, Text } from '../../../../../common';
|
||||
import { Column, Grid, Text } from '../../../../../common';
|
||||
import { useCatalog } from '../../../../../hooks';
|
||||
import { CatalogPurchaseWidgetView } from '../widgets/CatalogPurchaseWidgetView';
|
||||
import { CatalogSpacesWidgetView } from '../widgets/CatalogSpacesWidgetView';
|
||||
@ -19,10 +19,10 @@ export const CatalogLayoutSpacesView: FC<CatalogLayoutProps> = props =>
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
<Column size={ 7 } overflow="hidden">
|
||||
<Column overflow="hidden" size={ 7 }>
|
||||
<CatalogSpacesWidgetView />
|
||||
</Column>
|
||||
<Column center={ !currentOffer } size={ 5 } overflow="hidden">
|
||||
<Column center={ !currentOffer } overflow="hidden" size={ 5 }>
|
||||
{ !currentOffer &&
|
||||
<>
|
||||
{ !!page.localization.getImage(1) && <img alt="" src={ page.localization.getImage(1) } /> }
|
||||
@ -30,14 +30,14 @@ export const CatalogLayoutSpacesView: FC<CatalogLayoutProps> = props =>
|
||||
</> }
|
||||
{ currentOffer &&
|
||||
<>
|
||||
<Base position="relative" overflow="hidden">
|
||||
<div className="position-relative overflow-hidden">
|
||||
<CatalogViewProductWidgetView />
|
||||
</Base>
|
||||
</div>
|
||||
<Column grow gap={ 1 }>
|
||||
<Text grow truncate>{ currentOffer.localizationName }</Text>
|
||||
<Flex justifyContent="end">
|
||||
<div className="flex justify-content-end">
|
||||
<CatalogTotalPriceWidget alignItems="end" />
|
||||
</Flex>
|
||||
</div>
|
||||
<CatalogPurchaseWidgetView />
|
||||
</Column>
|
||||
</> }
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { Column, Flex, Grid, Text } from '../../../../../common';
|
||||
import { Column, Grid, Text } from '../../../../../common';
|
||||
import { useCatalog } from '../../../../../hooks';
|
||||
import { CatalogItemGridWidgetView } from '../widgets/CatalogItemGridWidgetView';
|
||||
import { CatalogPurchaseWidgetView } from '../widgets/CatalogPurchaseWidgetView';
|
||||
@ -29,11 +29,11 @@ export const CatalogLayoutTrophiesView: FC<CatalogLayoutProps> = props =>
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
<Column size={ 7 } overflow="hidden">
|
||||
<Column overflow="hidden" size={ 7 }>
|
||||
<CatalogItemGridWidgetView />
|
||||
<textarea className="flex-grow-1 form-control w-100" defaultValue={ trophyText || '' } onChange={ event => setTrophyText(event.target.value) } />
|
||||
</Column>
|
||||
<Column center={ !currentOffer } size={ 5 } overflow="hidden">
|
||||
<Column center={ !currentOffer } overflow="hidden" size={ 5 }>
|
||||
{ !currentOffer &&
|
||||
<>
|
||||
{ !!page.localization.getImage(1) && <img alt="" src={ page.localization.getImage(1) } /> }
|
||||
@ -44,9 +44,9 @@ export const CatalogLayoutTrophiesView: FC<CatalogLayoutProps> = props =>
|
||||
<CatalogViewProductWidgetView />
|
||||
<Column grow gap={ 1 }>
|
||||
<Text grow truncate>{ currentOffer.localizationName }</Text>
|
||||
<Flex justifyContent="end">
|
||||
<div className="flex justify-content-end">
|
||||
<CatalogTotalPriceWidget alignItems="end" />
|
||||
</Flex>
|
||||
</div>
|
||||
<CatalogPurchaseWidgetView />
|
||||
</Column>
|
||||
</> }
|
||||
|
@ -2,7 +2,7 @@ import { ClubOfferData, GetClubOffersMessageComposer, PurchaseFromCatalogCompose
|
||||
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { CatalogPurchaseState, LocalizeText, SendMessageComposer } from '../../../../../api';
|
||||
import { AutoGrid, Button, Column, Flex, Grid, LayoutCurrencyIcon, LayoutGridItem, LayoutLoadingSpinnerView, Text } from '../../../../../common';
|
||||
import { CatalogEvent, CatalogPurchasedEvent, CatalogPurchaseFailureEvent } from '../../../../../events';
|
||||
import { CatalogEvent, CatalogPurchaseFailureEvent, CatalogPurchasedEvent } from '../../../../../events';
|
||||
import { useCatalog, usePurse, useUiEvent } from '../../../../../hooks';
|
||||
import { CatalogLayoutProps } from './CatalogLayout.types';
|
||||
|
||||
@ -114,9 +114,9 @@ export const CatalogLayoutVipBuyView: FC<CatalogLayoutProps> = props =>
|
||||
case CatalogPurchaseState.CONFIRM:
|
||||
return <Button fullWidth variant="warning" onClick={ purchaseSubscription }>{ LocalizeText('catalog.marketplace.confirm_title') }</Button>;
|
||||
case CatalogPurchaseState.PURCHASE:
|
||||
return <Button fullWidth variant="primary" disabled><LayoutLoadingSpinnerView /></Button>;
|
||||
return <Button disabled fullWidth variant="primary"><LayoutLoadingSpinnerView /></Button>;
|
||||
case CatalogPurchaseState.FAILED:
|
||||
return <Button fullWidth variant="danger" disabled>{ LocalizeText('generic.failed') }</Button>;
|
||||
return <Button disabled fullWidth variant="danger">{ LocalizeText('generic.failed') }</Button>;
|
||||
case CatalogPurchaseState.NONE:
|
||||
default:
|
||||
return <Button fullWidth variant="success" onClick={ () => setPurchaseState(CatalogPurchaseState.CONFIRM) }>{ LocalizeText('buy') }</Button>;
|
||||
@ -130,23 +130,23 @@ export const CatalogLayoutVipBuyView: FC<CatalogLayoutProps> = props =>
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
<Column fullHeight size={ 7 } overflow="hidden" justifyContent="between">
|
||||
<AutoGrid columnCount={ 1 } className="nitro-catalog-layout-vip-buy-grid">
|
||||
<Column fullHeight justifyContent="between" overflow="hidden" size={ 7 }>
|
||||
<AutoGrid className="nitro-catalog-layout-vip-buy-grid" columnCount={ 1 }>
|
||||
{ clubOffers && (clubOffers.length > 0) && clubOffers.map((offer, index) =>
|
||||
{
|
||||
return (
|
||||
<LayoutGridItem key={ index } column={ false } center={ false } alignItems="center" justifyContent="between" itemActive={ pendingOffer === offer } className="p-1" onClick={ () => setOffer(offer) }>
|
||||
<LayoutGridItem key={ index } alignItems="center" center={ false } className="p-1" column={ false } itemActive={ pendingOffer === offer } justifyContent="between" onClick={ () => setOffer(offer) }>
|
||||
<i className="icon-hc-banner" />
|
||||
<Column justifyContent="end" gap={ 0 }>
|
||||
<Column gap={ 0 } justifyContent="end">
|
||||
<Text textEnd>{ getOfferText(offer) }</Text>
|
||||
<Flex justifyContent="end" gap={ 1 }>
|
||||
<Flex gap={ 1 } justifyContent="end">
|
||||
{ (offer.priceCredits > 0) &&
|
||||
<Flex alignItems="center" justifyContent="end" gap={ 1 }>
|
||||
<Flex alignItems="center" gap={ 1 } justifyContent="end">
|
||||
<Text>{ offer.priceCredits }</Text>
|
||||
<LayoutCurrencyIcon type={ -1 } />
|
||||
</Flex> }
|
||||
{ (offer.priceActivityPoints > 0) &&
|
||||
<Flex alignItems="center" justifyContent="end" gap={ 1 }>
|
||||
<Flex alignItems="center" gap={ 1 } justifyContent="end">
|
||||
<Text>{ offer.priceActivityPoints }</Text>
|
||||
<LayoutCurrencyIcon type={ offer.priceActivityPointsType } />
|
||||
</Flex> }
|
||||
@ -158,10 +158,10 @@ export const CatalogLayoutVipBuyView: FC<CatalogLayoutProps> = props =>
|
||||
</AutoGrid>
|
||||
<Text center dangerouslySetInnerHTML={ { __html: LocalizeText('catalog.vip.buy.hccenter') } }></Text>
|
||||
</Column>
|
||||
<Column size={ 5 } overflow="hidden">
|
||||
<Column fullHeight center overflow="hidden">
|
||||
<Column overflow="hidden" size={ 5 }>
|
||||
<Column center fullHeight overflow="hidden">
|
||||
{ currentPage.localization.getImage(1) && <img alt="" src={ currentPage.localization.getImage(1) } /> }
|
||||
<Text center overflow="auto" dangerouslySetInnerHTML={ { __html: getSubscriptionDetails } } />
|
||||
<Text center dangerouslySetInnerHTML={ { __html: getSubscriptionDetails } } overflow="auto" />
|
||||
</Column>
|
||||
{ pendingOffer &&
|
||||
<Column fullWidth grow justifyContent="end">
|
||||
@ -170,18 +170,18 @@ export const CatalogLayoutVipBuyView: FC<CatalogLayoutProps> = props =>
|
||||
<Text fontWeight="bold">{ getPurchaseHeader() }</Text>
|
||||
<Text>{ getPurchaseValidUntil() }</Text>
|
||||
</Column>
|
||||
<Column gap={ 1 }>
|
||||
<div className="flex flex-column gap-1">
|
||||
{ (pendingOffer.priceCredits > 0) &&
|
||||
<Flex alignItems="center" justifyContent="end" gap={ 1 }>
|
||||
<Flex alignItems="center" gap={ 1 } justifyContent="end">
|
||||
<Text>{ pendingOffer.priceCredits }</Text>
|
||||
<LayoutCurrencyIcon type={ -1 } />
|
||||
</Flex> }
|
||||
{ (pendingOffer.priceActivityPoints > 0) &&
|
||||
<Flex alignItems="center" justifyContent="end" gap={ 1 }>
|
||||
<Flex alignItems="center" gap={ 1 } justifyContent="end">
|
||||
<Text>{ pendingOffer.priceActivityPoints }</Text>
|
||||
<LayoutCurrencyIcon type={ pendingOffer.priceActivityPointsType } />
|
||||
</Flex> }
|
||||
</Column>
|
||||
</div>
|
||||
</Flex>
|
||||
{ getPurchaseButton() }
|
||||
</Column> }
|
||||
|
@ -27,8 +27,8 @@ export const CatalogLayoutFrontPageItemView: FC<CatalogLayoutFrontPageItemViewPr
|
||||
const imageUrl = (GetConfigurationValue<string>('image.library.url') + item.itemPromoImage);
|
||||
|
||||
return (
|
||||
<LayoutBackgroundImage imageUrl={ imageUrl } classNames={ getClassNames } position={ position } fullHeight={ fullHeight } pointer={ pointer } overflow={ overflow } { ...rest }>
|
||||
<Text position="absolute" variant="white" className="bg-dark rounded p-2 m-2 bottom-0">
|
||||
<LayoutBackgroundImage classNames={ getClassNames } fullHeight={ fullHeight } imageUrl={ imageUrl } overflow={ overflow } pointer={ pointer } position={ position } { ...rest }>
|
||||
<Text className="bg-dark rounded p-2 m-2 bottom-0" position="absolute" variant="white">
|
||||
{ item.itemName }
|
||||
</Text>
|
||||
{ children }
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { FC, useCallback, useMemo } from 'react';
|
||||
import { GetImageIconUrlForProduct, LocalizeText, MarketplaceOfferData, MarketPlaceOfferState, ProductTypeEnum } from '../../../../../../api';
|
||||
import { GetImageIconUrlForProduct, LocalizeText, MarketPlaceOfferState, MarketplaceOfferData, ProductTypeEnum } from '../../../../../../api';
|
||||
import { Button, Column, LayoutGridItem, Text } from '../../../../../../common';
|
||||
|
||||
export interface MarketplaceItemViewProps
|
||||
@ -46,7 +46,7 @@ export const CatalogLayoutMarketplaceItemView: FC<MarketplaceItemViewProps> = pr
|
||||
}, [ offerData ]);
|
||||
|
||||
return (
|
||||
<LayoutGridItem shrink center={ false } column={ false } alignItems="center" className="p-1">
|
||||
<LayoutGridItem shrink alignItems="center" center={ false } className="p-1" column={ false }>
|
||||
<Column style={ { width: 40, height: 40 } }>
|
||||
<LayoutGridItem column={ false } itemImage={ GetImageIconUrlForProduct(((offerData.furniType === MarketplaceOfferData.TYPE_FLOOR) ? ProductTypeEnum.FLOOR : ProductTypeEnum.WALL), offerData.furniId, offerData.extraData) } itemUniqueNumber={ offerData.isUniqueLimitedItem ? offerData.stuffData.uniqueNumber : 0 } />
|
||||
</Column>
|
||||
@ -63,7 +63,7 @@ export const CatalogLayoutMarketplaceItemView: FC<MarketplaceItemViewProps> = pr
|
||||
<Text>{ LocalizeText('catalog.marketplace.offer_count', [ 'count' ], [ offerData.offerCount.toString() ]) }</Text>
|
||||
</> }
|
||||
</Column>
|
||||
<Column gap={ 1 }>
|
||||
<div className="flex flex-column gap-1">
|
||||
{ ((type === OWN_OFFER) && (offerData.status !== MarketPlaceOfferState.SOLD)) &&
|
||||
<Button variant="secondary" onClick={ () => onClick(offerData) }>
|
||||
{ LocalizeText('catalog.marketplace.offer.pick') }
|
||||
@ -73,11 +73,11 @@ export const CatalogLayoutMarketplaceItemView: FC<MarketplaceItemViewProps> = pr
|
||||
<Button variant="secondary" onClick={ () => onClick(offerData) }>
|
||||
{ LocalizeText('buy') }
|
||||
</Button>
|
||||
<Button variant="secondary" disabled>
|
||||
<Button disabled variant="secondary">
|
||||
{ LocalizeText('catalog.marketplace.view_more') }
|
||||
</Button>
|
||||
</> }
|
||||
</Column>
|
||||
</div>
|
||||
</LayoutGridItem>
|
||||
);
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ export const CatalogLayoutMarketplaceOwnItemsView: FC<CatalogLayoutProps> = prop
|
||||
{ LocalizeText('catalog.marketplace.redeem.no_sold_items') }
|
||||
</Text> }
|
||||
{ (creditsWaiting > 0) &&
|
||||
<Column center gap={ 1 } className="bg-muted rounded p-2">
|
||||
<Column center className="bg-muted rounded p-2" gap={ 1 }>
|
||||
<Text>
|
||||
{ LocalizeText('catalog.marketplace.redeem.get_credits', [ 'count', 'credits' ], [ soldOffers.length.toString(), creditsWaiting.toString() ]) }
|
||||
</Text>
|
||||
@ -90,10 +90,10 @@ export const CatalogLayoutMarketplaceOwnItemsView: FC<CatalogLayoutProps> = prop
|
||||
</Button>
|
||||
</Column> }
|
||||
<Column gap={ 1 } overflow="hidden">
|
||||
<Text truncate shrink fontWeight="bold">
|
||||
<Text shrink truncate fontWeight="bold">
|
||||
{ LocalizeText('catalog.marketplace.items_found', [ 'count' ], [ offers.length.toString() ]) }
|
||||
</Text>
|
||||
<Column overflow="auto" className="nitro-catalog-layout-marketplace-grid">
|
||||
<Column className="nitro-catalog-layout-marketplace-grid" overflow="auto">
|
||||
{ (offers.length > 0) && offers.map(offer => <CatalogLayoutMarketplaceItemView key={ offer.offerId } offerData={ offer } type={ OWN_OFFER } onClick={ takeItemBack } />) }
|
||||
</Column>
|
||||
</Column>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { BuyMarketplaceOfferMessageComposer, GetMarketplaceOffersMessageComposer, MarketplaceBuyOfferResultEvent, MarketPlaceOffersEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useMemo, useState } from 'react';
|
||||
import { IMarketplaceSearchOptions, LocalizeText, MarketplaceOfferData, MarketplaceSearchType, NotificationAlertType, SendMessageComposer } from '../../../../../../api';
|
||||
import { Button, ButtonGroup, Column, Text } from '../../../../../../common';
|
||||
import { Button, Column, Text } from '../../../../../../common';
|
||||
import { useMessageEvent, useNotification, usePurse } from '../../../../../../hooks';
|
||||
import { CatalogLayoutProps } from '../CatalogLayout.types';
|
||||
import { CatalogLayoutMarketplaceItemView, PUBLIC_OFFER } from './CatalogLayoutMarketplaceItemView';
|
||||
@ -134,7 +134,7 @@ export const CatalogLayoutMarketplacePublicItemsView: FC<CatalogLayoutMarketplac
|
||||
|
||||
return (
|
||||
<>
|
||||
<ButtonGroup>
|
||||
<div className="btn-group">
|
||||
<Button active={ (searchType === MarketplaceSearchType.BY_ACTIVITY) } onClick={ () => setSearchType(MarketplaceSearchType.BY_ACTIVITY) }>
|
||||
{ LocalizeText('catalog.marketplace.search_by_activity') }
|
||||
</Button>
|
||||
@ -144,10 +144,10 @@ export const CatalogLayoutMarketplacePublicItemsView: FC<CatalogLayoutMarketplac
|
||||
<Button active={ (searchType === MarketplaceSearchType.ADVANCED) } onClick={ () => setSearchType(MarketplaceSearchType.ADVANCED) }>
|
||||
{ LocalizeText('catalog.marketplace.search_advanced') }
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
<SearchFormView sortTypes={ getSortTypes } searchType={ searchType } onSearch={ requestOffers } />
|
||||
</div>
|
||||
<SearchFormView searchType={ searchType } sortTypes={ getSortTypes } onSearch={ requestOffers } />
|
||||
<Column gap={ 1 } overflow="hidden">
|
||||
<Text truncate shrink fontWeight="bold">
|
||||
<Text shrink truncate fontWeight="bold">
|
||||
{ LocalizeText('catalog.marketplace.items_found', [ 'count' ], [ offers.size.toString() ]) }
|
||||
</Text>
|
||||
<Column className="nitro-catalog-layout-marketplace-grid" overflow="auto">
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { IMarketplaceSearchOptions, LocalizeText, MarketplaceSearchType } from '../../../../../../api';
|
||||
import { Button, Column, Flex, Text } from '../../../../../../common';
|
||||
import { Button, Text } from '../../../../../../common';
|
||||
|
||||
export interface SearchFormViewProps
|
||||
{
|
||||
@ -44,28 +44,28 @@ export const SearchFormView: FC<SearchFormViewProps> = props =>
|
||||
}, [ onSearch, searchType, sortTypes ]);
|
||||
|
||||
return (
|
||||
<Column gap={ 1 }>
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
<div className="flex flex-column gap-1">
|
||||
<div className="flex items-center gap-1">
|
||||
<Text className="col-3">{ LocalizeText('catalog.marketplace.sort_order') }</Text>
|
||||
<select className="form-select form-select-sm" value={ sortType } onChange={ event => onSortTypeChange(parseInt(event.target.value)) }>
|
||||
{ sortTypes.map(type => <option key={ type } value={ type }>{ LocalizeText(`catalog.marketplace.sort.${ type }`) }</option>) }
|
||||
</select>
|
||||
</Flex>
|
||||
</div>
|
||||
{ searchType === MarketplaceSearchType.ADVANCED &&
|
||||
<>
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
<div className="flex items-center gap-1">
|
||||
<Text className="col-3">{ LocalizeText('catalog.marketplace.search_name') }</Text>
|
||||
<input className="form-control form-control-sm" type="text" value={ searchQuery } onChange={ event => setSearchQuery(event.target.value) }/>
|
||||
</Flex>
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<Text className="col-3">{ LocalizeText('catalog.marketplace.search_price') }</Text>
|
||||
<Flex fullWidth gap={ 1 }>
|
||||
<input className="form-control form-control-sm" type="number" min={ 0 } value={ min } onChange={ event => setMin(event.target.valueAsNumber) } />
|
||||
<input className="form-control form-control-sm" type="number" min={ 0 } value={ max } onChange={ event => setMax(event.target.valueAsNumber) } />
|
||||
</Flex>
|
||||
</Flex>
|
||||
<Button variant="secondary" className="mx-auto" onClick={ onClickSearch }>{ LocalizeText('generic.search') }</Button>
|
||||
<div className="flex w-100 gap-1">
|
||||
<input className="form-control form-control-sm" min={ 0 } type="number" value={ min } onChange={ event => setMin(event.target.valueAsNumber) } />
|
||||
<input className="form-control form-control-sm" min={ 0 } type="number" value={ max } onChange={ event => setMax(event.target.valueAsNumber) } />
|
||||
</div>
|
||||
</div>
|
||||
<Button className="mx-auto" variant="secondary" onClick={ onClickSearch }>{ LocalizeText('generic.search') }</Button>
|
||||
</> }
|
||||
</Column>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { GetMarketplaceConfigurationMessageComposer, MakeOfferMessageComposer, MarketplaceConfigurationEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { FurnitureItem, LocalizeText, ProductTypeEnum, SendMessageComposer } from '../../../../../../api';
|
||||
import { Base, Button, Column, Grid, LayoutFurniImageView, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../../../common';
|
||||
import { Button, Column, Grid, LayoutFurniImageView, NitroCardContentView, NitroCardHeaderView, NitroCardView, Text } from '../../../../../../common';
|
||||
import { CatalogPostMarketplaceOfferEvent } from '../../../../../../events';
|
||||
import { useCatalog, useMessageEvent, useNotification, useUiEvent } from '../../../../../../hooks';
|
||||
|
||||
@ -82,32 +82,32 @@ export const MarketplacePostOfferView : FC<{}> = props =>
|
||||
<NitroCardHeaderView headerText={ LocalizeText('inventory.marketplace.make_offer.title') } onCloseClick={ event => setItem(null) } />
|
||||
<NitroCardContentView overflow="hidden">
|
||||
<Grid fullHeight>
|
||||
<Column center className="bg-muted rounded p-2" size={ 4 } overflow="hidden">
|
||||
<LayoutFurniImageView productType={ item.isWallItem ? ProductTypeEnum.WALL : ProductTypeEnum.FLOOR } productClassId={ item.type } extraData={ item.extra.toString() } />
|
||||
<Column center className="bg-muted rounded p-2" overflow="hidden" size={ 4 }>
|
||||
<LayoutFurniImageView extraData={ item.extra.toString() } productClassId={ item.type } productType={ item.isWallItem ? ProductTypeEnum.WALL : ProductTypeEnum.FLOOR } />
|
||||
</Column>
|
||||
<Column size={ 8 } justifyContent="between" overflow="hidden">
|
||||
<Column justifyContent="between" overflow="hidden" size={ 8 }>
|
||||
<Column grow gap={ 1 }>
|
||||
<Text fontWeight="bold">{ getFurniTitle }</Text>
|
||||
<Text truncate shrink>{ getFurniDescription }</Text>
|
||||
<Text shrink truncate>{ getFurniDescription }</Text>
|
||||
</Column>
|
||||
<Column overflow="auto">
|
||||
<Text italics>
|
||||
{ LocalizeText('inventory.marketplace.make_offer.expiration_info', [ 'time' ], [ marketplaceConfiguration.offerTime.toString() ]) }
|
||||
</Text>
|
||||
<div className="input-group has-validation">
|
||||
<input className="form-control form-control-sm" type="number" min={ 0 } value={ tempAskingPrice } onChange={ event => updateAskingPrice(event.target.value) } placeholder={ LocalizeText('inventory.marketplace.make_offer.price_request') } />
|
||||
<input className="form-control form-control-sm" min={ 0 } placeholder={ LocalizeText('inventory.marketplace.make_offer.price_request') } type="number" value={ tempAskingPrice } onChange={ event => updateAskingPrice(event.target.value) } />
|
||||
{ ((askingPrice < marketplaceConfiguration.minimumPrice) || isNaN(askingPrice)) &&
|
||||
<Base className="invalid-feedback d-block">
|
||||
<div className="invalid-feedback d-block">
|
||||
{ LocalizeText('inventory.marketplace.make_offer.min_price', [ 'minprice' ], [ marketplaceConfiguration.minimumPrice.toString() ]) }
|
||||
</Base> }
|
||||
</div> }
|
||||
{ ((askingPrice > marketplaceConfiguration.maximumPrice) && !isNaN(askingPrice)) &&
|
||||
<Base className="invalid-feedback d-block">
|
||||
<div className="invalid-feedback d-block">
|
||||
{ LocalizeText('inventory.marketplace.make_offer.max_price', [ 'maxprice' ], [ marketplaceConfiguration.maximumPrice.toString() ]) }
|
||||
</Base> }
|
||||
</div> }
|
||||
{ (!((askingPrice < marketplaceConfiguration.minimumPrice) || (askingPrice > marketplaceConfiguration.maximumPrice) || isNaN(askingPrice))) &&
|
||||
<Base className="invalid-feedback d-block">
|
||||
<div className="invalid-feedback d-block">
|
||||
{ LocalizeText('inventory.marketplace.make_offer.final_price', [ 'commission', 'finalprice' ], [ getCommission().toString(), (askingPrice + getCommission()).toString() ]) }
|
||||
</Base> }
|
||||
</div> }
|
||||
</div>
|
||||
<Button disabled={ ((askingPrice < marketplaceConfiguration.minimumPrice) || (askingPrice > marketplaceConfiguration.maximumPrice) || isNaN(askingPrice)) } onClick={ postItem }>
|
||||
{ LocalizeText('inventory.marketplace.make_offer.post') }
|
||||
|
@ -2,7 +2,7 @@ import { ApproveNameMessageComposer, ApproveNameMessageEvent, ColorConverter, Ge
|
||||
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { FaFillDrip } from 'react-icons/fa';
|
||||
import { DispatchUiEvent, GetPetAvailableColors, GetPetIndexFromLocalization, LocalizeText, SendMessageComposer } from '../../../../../../api';
|
||||
import { AutoGrid, Base, Button, Column, Flex, Grid, LayoutGridItem, LayoutPetImageView, Text } from '../../../../../../common';
|
||||
import { AutoGrid, Button, Column, Grid, LayoutGridItem, LayoutPetImageView, Text } from '../../../../../../common';
|
||||
import { CatalogPurchaseFailureEvent } from '../../../../../../events';
|
||||
import { useCatalog, useMessageEvent } from '../../../../../../hooks';
|
||||
import { CatalogAddOnBadgeWidgetView } from '../../widgets/CatalogAddOnBadgeWidgetView';
|
||||
@ -195,20 +195,20 @@ export const CatalogLayoutPetView: FC<CatalogLayoutProps> = props =>
|
||||
|
||||
return (
|
||||
<Grid>
|
||||
<Column size={ 7 } overflow="hidden">
|
||||
<Column overflow="hidden" size={ 7 }>
|
||||
<AutoGrid columnCount={ 5 }>
|
||||
{ !colorsShowing && (sellablePalettes.length > 0) && sellablePalettes.map((palette, index) =>
|
||||
{
|
||||
return (
|
||||
<LayoutGridItem key={ index } itemActive={ (selectedPaletteIndex === index) } onClick={ event => setSelectedPaletteIndex(index) }>
|
||||
<LayoutPetImageView typeId={ petIndex } paletteId={ palette.paletteId } direction={ 2 } headOnly={ true } />
|
||||
<LayoutPetImageView direction={ 2 } headOnly={ true } paletteId={ palette.paletteId } typeId={ petIndex } />
|
||||
</LayoutGridItem>
|
||||
);
|
||||
}) }
|
||||
{ colorsShowing && (sellableColors.length > 0) && sellableColors.map((colorSet, index) => <LayoutGridItem itemHighlight key={ index } itemActive={ (selectedColorIndex === index) } itemColor={ ColorConverter.int2rgb(colorSet[0]) } className="clear-bg" onClick={ event => setSelectedColorIndex(index) } />) }
|
||||
{ colorsShowing && (sellableColors.length > 0) && sellableColors.map((colorSet, index) => <LayoutGridItem key={ index } itemHighlight className="clear-bg" itemActive={ (selectedColorIndex === index) } itemColor={ ColorConverter.int2rgb(colorSet[0]) } onClick={ event => setSelectedColorIndex(index) } />) }
|
||||
</AutoGrid>
|
||||
</Column>
|
||||
<Column center={ !currentOffer } size={ 5 } overflow="hidden">
|
||||
<Column center={ !currentOffer } overflow="hidden" size={ 5 }>
|
||||
{ !currentOffer &&
|
||||
<>
|
||||
{ !!page.localization.getImage(1) && <img alt="" src={ page.localization.getImage(1) } /> }
|
||||
@ -216,24 +216,24 @@ export const CatalogLayoutPetView: FC<CatalogLayoutProps> = props =>
|
||||
</> }
|
||||
{ currentOffer &&
|
||||
<>
|
||||
<Base position="relative" overflow="hidden">
|
||||
<div className="position-relative overflow-hidden">
|
||||
<CatalogViewProductWidgetView />
|
||||
<CatalogAddOnBadgeWidgetView position="absolute" className="bg-muted rounded bottom-1 end-1" />
|
||||
<CatalogAddOnBadgeWidgetView className="bg-muted rounded bottom-1 end-1" position="absolute" />
|
||||
{ ((petIndex > -1) && (petIndex <= 7)) &&
|
||||
<Button position="absolute" className="bottom-1 start-1" onClick={ event => setColorsShowing(!colorsShowing) }>
|
||||
<Button className="bottom-1 start-1" position="absolute" onClick={ event => setColorsShowing(!colorsShowing) }>
|
||||
<FaFillDrip className="fa-icon" />
|
||||
</Button> }
|
||||
</Base>
|
||||
</div>
|
||||
<Column grow gap={ 1 }>
|
||||
<Text truncate>{ petBreedName }</Text>
|
||||
<Column grow gap={ 1 }>
|
||||
<input type="text" className="form-control form-control-sm w-100" placeholder={ LocalizeText('widgets.petpackage.name.title') } value={ petName } onChange={ event => setPetName(event.target.value) } />
|
||||
<input className="form-control form-control-sm w-100" placeholder={ LocalizeText('widgets.petpackage.name.title') } type="text" value={ petName } onChange={ event => setPetName(event.target.value) } />
|
||||
{ (approvalResult > 0) &&
|
||||
<Base className="invalid-feedback d-block m-0">{ validationErrorMessage }</Base> }
|
||||
<div className="invalid-feedback d-block m-0">{ validationErrorMessage }</div> }
|
||||
</Column>
|
||||
<Flex justifyContent="end">
|
||||
<CatalogTotalPriceWidget justifyContent="end" alignItems="end" />
|
||||
</Flex>
|
||||
<div className="flex justify-content-end">
|
||||
<CatalogTotalPriceWidget alignItems="end" justifyContent="end" />
|
||||
</div>
|
||||
<CatalogPurchaseWidgetView purchaseCallback={ purchasePet } />
|
||||
</Column>
|
||||
</> }
|
||||
|
@ -53,9 +53,9 @@ export const CatalogLayoutVipGiftsView: FC<CatalogLayoutProps> = props =>
|
||||
|
||||
return (
|
||||
<>
|
||||
<Text truncate shrink fontWeight="bold">{ giftsAvailable() }</Text>
|
||||
<AutoGrid columnCount={ 1 } className="nitro-catalog-layout-vip-gifts-grid">
|
||||
{ (clubGifts.offers.length > 0) && sortGifts.map(offer => <VipGiftItem key={ offer.offerId } offer={ offer } isAvailable={ (clubGifts.getOfferExtraData(offer.offerId).isSelectable && (clubGifts.giftsAvailable > 0)) } onSelect={ selectGift } daysRequired={ clubGifts.getOfferExtraData(offer.offerId).daysRequired }/>) }
|
||||
<Text shrink truncate fontWeight="bold">{ giftsAvailable() }</Text>
|
||||
<AutoGrid className="nitro-catalog-layout-vip-gifts-grid" columnCount={ 1 }>
|
||||
{ (clubGifts.offers.length > 0) && sortGifts.map(offer => <VipGiftItem key={ offer.offerId } daysRequired={ clubGifts.getOfferExtraData(offer.offerId).daysRequired } isAvailable={ (clubGifts.getOfferExtraData(offer.offerId).isSelectable && (clubGifts.giftsAvailable > 0)) } offer={ offer } onSelect={ selectGift }/>) }
|
||||
</AutoGrid>
|
||||
</>
|
||||
)
|
||||
|
@ -52,10 +52,10 @@ export const VipGiftItem : FC<VipGiftItemViewProps> = props =>
|
||||
},[ daysRequired ]);
|
||||
|
||||
return (
|
||||
<LayoutGridItem center={ false } column={ false } alignItems="center" className="p-1">
|
||||
<LayoutGridItem alignItems="center" center={ false } className="p-1" column={ false }>
|
||||
<LayoutImage imageUrl={ getImageUrlForOffer() } />
|
||||
<Text grow fontWeight="bold">{ getItemTitle() }</Text>
|
||||
<Button variant="secondary" onClick={ () => onSelect(offer.localizationId) } disabled={ !isAvailable }>
|
||||
<Button disabled={ !isAvailable } variant="secondary" onClick={ () => onSelect(offer.localizationId) }>
|
||||
{ LocalizeText('catalog.club_gift.select') }
|
||||
</Button>
|
||||
</LayoutGridItem>
|
||||
|
@ -21,8 +21,8 @@ export const CatalogBundleGridWidgetView: FC<CatalogBundleGridWidgetViewProps> =
|
||||
if(!currentOffer) return null;
|
||||
|
||||
return (
|
||||
<AutoGrid innerRef={ elementRef } columnCount={ 5 } { ...rest }>
|
||||
{ currentOffer.products && (currentOffer.products.length > 0) && currentOffer.products.map((product, index) => <LayoutGridItem key={ index } itemImage={ product.getIconUrl() } itemCount={ product.productCount } />) }
|
||||
<AutoGrid columnCount={ 5 } innerRef={ elementRef } { ...rest }>
|
||||
{ currentOffer.products && (currentOffer.products.length > 0) && currentOffer.products.map((product, index) => <LayoutGridItem key={ index } itemCount={ product.productCount } itemImage={ product.getIconUrl() } />) }
|
||||
{ children }
|
||||
</AutoGrid>
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { CatalogGroupsComposer, StringDataType } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect, useMemo, useState } from 'react';
|
||||
import { LocalizeText, SendMessageComposer } from '../../../../../api';
|
||||
import { Base, Button, Flex } from '../../../../../common';
|
||||
import { Button, Flex } from '../../../../../common';
|
||||
import { useCatalog } from '../../../../../hooks';
|
||||
|
||||
export const CatalogGuildSelectorWidgetView: FC<{}> = props =>
|
||||
@ -49,27 +49,27 @@ export const CatalogGuildSelectorWidgetView: FC<{}> = props =>
|
||||
if(!groups || !groups.length)
|
||||
{
|
||||
return (
|
||||
<Base className="bg-muted rounded p-1 text-black text-center">
|
||||
<div className="bg-muted rounded p-1 text-black text-center">
|
||||
{ LocalizeText('catalog.guild_selector.members_only') }
|
||||
<Button className="mt-1">
|
||||
{ LocalizeText('catalog.guild_selector.find_groups') }
|
||||
</Button>
|
||||
</Base>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const selectedGroup = groups[selectedGroupIndex];
|
||||
|
||||
return (
|
||||
<Flex gap={ 1 }>
|
||||
<div className="flex gap-1">
|
||||
{ !!selectedGroup &&
|
||||
<Flex overflow="hidden" className="rounded border">
|
||||
<Base fullHeight style={ { width: '20px', backgroundColor: '#' + selectedGroup.colorA } } />
|
||||
<Base fullHeight style={ { width: '20px', backgroundColor: '#' + selectedGroup.colorB } } />
|
||||
<Flex className="rounded border" overflow="hidden">
|
||||
<div className="h-100" style={ { width: '20px', backgroundColor: '#' + selectedGroup.colorA } } />
|
||||
<div className="h-100" style={ { width: '20px', backgroundColor: '#' + selectedGroup.colorB } } />
|
||||
</Flex> }
|
||||
<select className="form-select form-select-sm" value={ selectedGroupIndex } onChange={ event => setSelectedGroupIndex(parseInt(event.target.value)) }>
|
||||
{ groups.map((group, index) => <option key={ index } value={ index }>{ group.groupName }</option>) }
|
||||
</select>
|
||||
</Flex>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ export const CatalogItemGridWidgetView: FC<CatalogItemGridWidgetViewProps> = pro
|
||||
}
|
||||
|
||||
return (
|
||||
<AutoGrid innerRef={ elementRef } columnCount={ columnCount } { ...rest }>
|
||||
<AutoGrid columnCount={ columnCount } innerRef={ elementRef } { ...rest }>
|
||||
{ currentPage.offers && (currentPage.offers.length > 0) && currentPage.offers.map((offer, index) => <CatalogGridOfferView key={ index } itemActive={ (currentOffer && (currentOffer.offerId === offer.offerId)) } offer={ offer } selectOffer={ selectOffer } />) }
|
||||
{ children }
|
||||
</AutoGrid>
|
||||
|
@ -1,19 +1,17 @@
|
||||
import { FC } from 'react';
|
||||
import { Offer } from '../../../../../api';
|
||||
import { Base, BaseProps, LayoutLimitedEditionCompletePlateView } from '../../../../../common';
|
||||
import { LayoutLimitedEditionCompletePlateView } from '../../../../../common';
|
||||
import { useCatalog } from '../../../../../hooks';
|
||||
|
||||
export const CatalogLimitedItemWidgetView: FC<BaseProps<HTMLDivElement>> = props =>
|
||||
export const CatalogLimitedItemWidgetView: FC = props =>
|
||||
{
|
||||
const { children = null, ...rest } = props;
|
||||
const { currentOffer = null } = useCatalog();
|
||||
|
||||
if(!currentOffer || (currentOffer.pricingModel !== Offer.PRICING_MODEL_SINGLE) || !currentOffer.product.isUniqueLimitedItem) return null;
|
||||
|
||||
return (
|
||||
<Base { ...rest }>
|
||||
<div className="w-100">
|
||||
<LayoutLimitedEditionCompletePlateView className="mx-auto" uniqueLimitedItemsLeft={ currentOffer.product.uniqueLimitedItemsLeft } uniqueLimitedSeriesSize={ currentOffer.product.uniqueLimitedItemSeriesSize } />
|
||||
{ children }
|
||||
</Base>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { FC } from 'react';
|
||||
import { FaPlus } from 'react-icons/fa';
|
||||
import { IPurchasableOffer } from '../../../../../api';
|
||||
import { Flex, LayoutCurrencyIcon, Text } from '../../../../../common';
|
||||
import { LayoutCurrencyIcon, Text } from '../../../../../common';
|
||||
import { useCatalog } from '../../../../../hooks';
|
||||
|
||||
interface CatalogPriceDisplayWidgetViewProps
|
||||
@ -21,17 +21,17 @@ export const CatalogPriceDisplayWidgetView: FC<CatalogPriceDisplayWidgetViewProp
|
||||
return (
|
||||
<>
|
||||
{ (offer.priceInCredits > 0) &&
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
<div className="flex items-center gap-1">
|
||||
<Text bold>{ (offer.priceInCredits * quantity) }</Text>
|
||||
<LayoutCurrencyIcon type={ -1 } />
|
||||
</Flex> }
|
||||
</div> }
|
||||
{ separator && (offer.priceInCredits > 0) && (offer.priceInActivityPoints > 0) &&
|
||||
<FaPlus size="xs" color="black" className="fa-icon" /> }
|
||||
<FaPlus className="fa-icon" color="black" size="xs" /> }
|
||||
{ (offer.priceInActivityPoints > 0) &&
|
||||
<Flex alignItems="center" gap={ 1 }>
|
||||
<div className="flex items-center gap-1">
|
||||
<Text bold>{ (offer.priceInActivityPoints * quantity) }</Text>
|
||||
<LayoutCurrencyIcon type={ offer.activityPointType } />
|
||||
</Flex> }
|
||||
</div> }
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user