mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-23 14:40:50 +01:00
Merge branch 'feature/hotelview' into dev
This commit is contained in:
commit
d0d9519fd1
@ -25,8 +25,20 @@
|
||||
],
|
||||
"hotelview": {
|
||||
"widgets": {
|
||||
"types": "news,",
|
||||
"slot1": "news"
|
||||
"slot.1.widget": "promoarticle",
|
||||
"slot.1.conf": "",
|
||||
"slot.2.widget": "widgetcontainer",
|
||||
"slot.2.conf": "image:${image.library.url}web_promo_small/spromo_Canal_Bundle.png,texts:2021NitroPromo,btnLink:https://google.com",
|
||||
"slot.3.widget": "promoarticle",
|
||||
"slot.3.conf": "",
|
||||
"slot.4.widget": "",
|
||||
"slot.4.conf": "",
|
||||
"slot.5.widget": "",
|
||||
"slot.5.conf": "",
|
||||
"slot.6.widget": "achievementcompetition_hall_of_fame",
|
||||
"slot.6.conf": "",
|
||||
"slot.7.widget": "",
|
||||
"slot.7.conf": ""
|
||||
},
|
||||
"images": {
|
||||
"background": "${asset.url}/images/reception/stretch_blue.png",
|
||||
|
@ -80,6 +80,7 @@ $cello-light: #21516e !default;
|
||||
$cello-dark: #1e465e !default;
|
||||
$pale-sky: #677181 !default;
|
||||
$oslo-gray: #8F9297 !default;
|
||||
$gainsboro: #d9d9d9 !default;
|
||||
$ghost: #c8cad0 !default;
|
||||
$gray-chateau: #a3a7b1 !default;
|
||||
$gable-green: #1C323F !default;
|
||||
@ -125,7 +126,8 @@ $theme-colors: (
|
||||
"white": $white,
|
||||
"black": $black,
|
||||
"muted": $muted,
|
||||
"purple": $purple
|
||||
"purple": $purple,
|
||||
"gainsboro": $gainsboro
|
||||
) !default;
|
||||
// scss-docs-end theme-colors-map
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
width: 100%;
|
||||
height: calc(100% - 55px);
|
||||
background: rgba($black, 1);
|
||||
color:#000;
|
||||
|
||||
.avatar-image {
|
||||
bottom: 12px;
|
||||
@ -13,6 +14,7 @@
|
||||
}
|
||||
|
||||
.background {
|
||||
top:0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-position: left;
|
||||
@ -73,4 +75,25 @@
|
||||
background-repeat: no-repeat;
|
||||
background-position: right top;
|
||||
}
|
||||
|
||||
.landing-widgets {
|
||||
z-index: 9;
|
||||
position: relative;
|
||||
|
||||
}
|
||||
|
||||
.widget-slot {
|
||||
z-index: 9;
|
||||
}
|
||||
|
||||
hr {
|
||||
background: $black;
|
||||
box-shadow: 0 1px rgba($white,.5);
|
||||
opacity: 0.5
|
||||
}
|
||||
}
|
||||
|
||||
@import './views/widgets/promo-article/PromoArticleWidgetView.scss';
|
||||
@import './views/widgets/bonus-rare/BonusRareWidgetView.scss';
|
||||
@import './views/widgets/hall-of-fame/HallOfFameWidgetView.scss';
|
||||
@import './views/widgets/widgetcontainer/WidgetContainerView.scss'
|
||||
|
@ -1,16 +1,18 @@
|
||||
import { RoomSessionEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useState } from 'react';
|
||||
import { GetConfiguration, GetNitroInstance } from '../../api';
|
||||
import { GetConfiguration, GetConfigurationManager } from '../../api';
|
||||
import { useRoomSessionManagerEvent } from '../../hooks/events/nitro/session/room-session-manager-event';
|
||||
import { HotelViewProps } from './HotelView.types';
|
||||
import { WidgetSlotView } from './views/widget-slot/WidgetSlotView';
|
||||
|
||||
export const HotelView: FC<HotelViewProps> = props =>
|
||||
{
|
||||
const [ isVisible, setIsVisible ] = useState(true);
|
||||
const [isVisible, setIsVisible] = useState(true);
|
||||
const widgetSlotCount = 7;
|
||||
|
||||
const onRoomSessionEvent = useCallback((event: RoomSessionEvent) =>
|
||||
{
|
||||
switch(event.type)
|
||||
switch (event.type)
|
||||
{
|
||||
case RoomSessionEvent.CREATED:
|
||||
setIsVisible(false);
|
||||
@ -24,24 +26,75 @@ export const HotelView: FC<HotelViewProps> = props =>
|
||||
useRoomSessionManagerEvent(RoomSessionEvent.CREATED, onRoomSessionEvent);
|
||||
useRoomSessionManagerEvent(RoomSessionEvent.ENDED, onRoomSessionEvent);
|
||||
|
||||
if(!isVisible) return null;
|
||||
if (!isVisible) return null;
|
||||
|
||||
const backgroundColor = GetConfiguration('hotelview')['images']['background.colour'];
|
||||
const background = GetNitroInstance().core.configuration.interpolate(GetConfiguration('hotelview')['images']['background']);
|
||||
const sun = GetNitroInstance().core.configuration.interpolate(GetConfiguration('hotelview')['images']['sun']);
|
||||
const drape = GetNitroInstance().core.configuration.interpolate(GetConfiguration('hotelview')['images']['drape']);
|
||||
const left = GetNitroInstance().core.configuration.interpolate(GetConfiguration('hotelview')['images']['left']);
|
||||
const rightRepeat = GetNitroInstance().core.configuration.interpolate(GetConfiguration('hotelview')['images']['right.repeat']);
|
||||
const right = GetNitroInstance().core.configuration.interpolate(GetConfiguration('hotelview')['images']['right']);
|
||||
const background = GetConfigurationManager().interpolate(GetConfiguration('hotelview')['images']['background']);
|
||||
const sun = GetConfigurationManager().interpolate(GetConfiguration('hotelview')['images']['sun']);
|
||||
const drape = GetConfigurationManager().interpolate(GetConfiguration('hotelview')['images']['drape']);
|
||||
const left = GetConfigurationManager().interpolate(GetConfiguration('hotelview')['images']['left']);
|
||||
const rightRepeat = GetConfigurationManager().interpolate(GetConfiguration('hotelview')['images']['right.repeat']);
|
||||
const right = GetConfigurationManager().interpolate(GetConfiguration('hotelview')['images']['right']);
|
||||
|
||||
return (
|
||||
<div className="nitro-hotel-view" style={ (backgroundColor && backgroundColor) ? { background: backgroundColor } : {} }>
|
||||
<div className="background position-absolute" style={ (background && background.length) ? { backgroundImage: `url(${ background })` } : {} } />
|
||||
<div className="sun position-absolute" style={ (sun && sun.length) ? { backgroundImage: `url(${ sun })` } : {} } />
|
||||
<div className="drape position-absolute" style={ (drape && drape.length) ? { backgroundImage: `url(${ drape })` } : {} } />
|
||||
<div className="left position-absolute" style={ (left && left.length) ? { backgroundImage: `url(${ left })` } : {} } />
|
||||
<div className="right-repeat position-absolute" style={ (rightRepeat && rightRepeat.length) ? { backgroundImage: `url(${ rightRepeat })` } : {} } />
|
||||
<div className="right position-absolute" style={ (right && right.length) ? { backgroundImage: `url(${ right })` } : {} } />
|
||||
<div className="nitro-hotel-view" style={(backgroundColor && backgroundColor) ? { background: backgroundColor } : {}}>
|
||||
<div className="container h-100 py-3 overflow-hidden landing-widgets">
|
||||
<div className="row h-100">
|
||||
<div className="col-9 h-100 d-flex flex-column">
|
||||
<WidgetSlotView
|
||||
widgetSlot={ 1 }
|
||||
widgetType={GetConfiguration('hotelview')['widgets']['slot.' + 1 + '.widget']}
|
||||
widgetConf={GetConfiguration('hotelview')['widgets']['slot.' + 1 + '.conf']}
|
||||
className="col-6"
|
||||
/>
|
||||
<div className="col-12 row mx-0">
|
||||
<WidgetSlotView
|
||||
widgetSlot={ 2 }
|
||||
widgetType={GetConfiguration('hotelview')['widgets']['slot.' + 2 + '.widget']}
|
||||
widgetConf={GetConfiguration('hotelview')['widgets']['slot.' + 2 + '.conf']}
|
||||
className="col-7"
|
||||
/>
|
||||
<WidgetSlotView
|
||||
widgetSlot={ 3 }
|
||||
widgetType={GetConfiguration('hotelview')['widgets']['slot.' + 3 + '.widget']}
|
||||
widgetConf={GetConfiguration('hotelview')['widgets']['slot.' + 3 + '.conf']}
|
||||
className="col-5"
|
||||
/>
|
||||
<WidgetSlotView
|
||||
widgetSlot={ 4 }
|
||||
widgetType={GetConfiguration('hotelview')['widgets']['slot.' + 4 + '.widget']}
|
||||
widgetConf={GetConfiguration('hotelview')['widgets']['slot.' + 4 + '.conf']}
|
||||
className="col-7"
|
||||
/>
|
||||
<WidgetSlotView
|
||||
widgetSlot={ 5 }
|
||||
widgetType={GetConfiguration('hotelview')['widgets']['slot.' + 5 + '.widget']}
|
||||
widgetConf={GetConfiguration('hotelview')['widgets']['slot.' + 5 + '.conf']}
|
||||
className="col-5"
|
||||
/>
|
||||
</div>
|
||||
<WidgetSlotView
|
||||
widgetSlot={ 6 }
|
||||
widgetType={GetConfiguration('hotelview')['widgets']['slot.' + 6 + '.widget']}
|
||||
widgetConf={GetConfiguration('hotelview')['widgets']['slot.' + 6 + '.conf']}
|
||||
className="mt-auto"
|
||||
/>
|
||||
</div>
|
||||
<div className="col-3 h-100">
|
||||
<WidgetSlotView
|
||||
widgetSlot={ 7 }
|
||||
widgetType={GetConfiguration('hotelview')['widgets']['slot.' + 7 + '.widget']}
|
||||
widgetConf={GetConfiguration('hotelview')['widgets']['slot.' + 7 +'.conf']}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="background position-absolute" style={(background && background.length) ? { backgroundImage: `url(${background})` } : {}} />
|
||||
<div className="sun position-absolute" style={(sun && sun.length) ? { backgroundImage: `url(${sun})` } : {}} />
|
||||
<div className="drape position-absolute" style={(drape && drape.length) ? { backgroundImage: `url(${drape})` } : {}} />
|
||||
<div className="left position-absolute" style={(left && left.length) ? { backgroundImage: `url(${left})` } : {}} />
|
||||
<div className="right-repeat position-absolute" style={(rightRepeat && rightRepeat.length) ? { backgroundImage: `url(${rightRepeat})` } : {}} />
|
||||
<div className="right position-absolute" style={(right && right.length) ? { backgroundImage: `url(${right})` } : {}} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
14
src/views/hotel-view/views/widget-slot/WidgetSlotView.tsx
Normal file
14
src/views/hotel-view/views/widget-slot/WidgetSlotView.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import { FC } from 'react';
|
||||
import { GetWidgetLayout } from '../widgets/GetWidgetLayout';
|
||||
import { WidgetSlotViewProps } from './WidgetSlotView.types';
|
||||
|
||||
export const WidgetSlotView: FC<WidgetSlotViewProps> = props =>
|
||||
{
|
||||
const { widgetType = null, widgetSlot = 0, widgetConf = null, className= '', ...rest } = props;
|
||||
|
||||
return (
|
||||
<div className={`widget-slot slot-${widgetSlot} ${(className || '')}`} { ...rest }>
|
||||
<GetWidgetLayout widgetType={widgetType} slot={widgetSlot} widgetConf={widgetConf} />
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
import { DetailsHTMLAttributes } from 'react';
|
||||
|
||||
export interface WidgetSlotViewProps extends DetailsHTMLAttributes<HTMLDivElement>
|
||||
{
|
||||
widgetType: string;
|
||||
widgetSlot: number;
|
||||
widgetConf: string;
|
||||
}
|
23
src/views/hotel-view/views/widgets/GetWidgetLayout.tsx
Normal file
23
src/views/hotel-view/views/widgets/GetWidgetLayout.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import { FC } from 'react';
|
||||
import { BonusRareWidgetView } from './bonus-rare/BonusRareWidgetView';
|
||||
import { GetWidgetLayoutProps } from './GetWidgetLayout.types';
|
||||
import { HallOfFameWidgetView } from './hall-of-fame/HallOfFameWidgetView';
|
||||
import { PromoArticleWidgetView } from './promo-article/PromoArticleWidgetView';
|
||||
import { WidgetContainerView } from './widgetcontainer/WIdgetContainerView';
|
||||
|
||||
export const GetWidgetLayout: FC<GetWidgetLayoutProps> = props =>
|
||||
{
|
||||
switch (props.widgetType)
|
||||
{
|
||||
case "promoarticle":
|
||||
return <PromoArticleWidgetView />;
|
||||
case "achievementcompetition_hall_of_fame":
|
||||
return <HallOfFameWidgetView slot={props.slot} conf={props.widgetConf} />;
|
||||
case "bonusrare":
|
||||
return <BonusRareWidgetView />;
|
||||
case "widgetcontainer":
|
||||
return <WidgetContainerView conf={props.widgetConf} />
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
export interface GetWidgetLayoutProps
|
||||
{
|
||||
widgetType: string;
|
||||
slot: number;
|
||||
widgetConf: string;
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
.bonus-rare {
|
||||
height: 100px;
|
||||
justify-content: center;
|
||||
|
||||
.bonus-bar-container {
|
||||
height: 30px;
|
||||
width: 300px;
|
||||
border: 2px ridge #e2e2e2;
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
import { BonusRareInfoMessageEvent, GetBonusRareInfoMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { CreateMessageHook, SendMessageHook } from '../../../../../hooks/messages/message-event';
|
||||
import { BonusRareWidgetViewProps } from './BonusRareWidgetView.types';
|
||||
|
||||
export const BonusRareWidgetView: FC<BonusRareWidgetViewProps> = props =>
|
||||
{
|
||||
const [ productType, setProductType ] = useState<string>(null);
|
||||
const [ productClassId, setProductClassId ] = useState<number>(null);
|
||||
const [ totalCoinsForBonus, setTotalCoinsForBonus ] = useState<number>(null);
|
||||
const [ coinsStillRequiredToBuy, setCoinsStillRequiredToBuy ] = useState<number>(null);
|
||||
|
||||
const onBonusRareInfoMessageEvent = useCallback((event: BonusRareInfoMessageEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setProductType(parser.productType);
|
||||
setProductClassId(parser.productClassId);
|
||||
setTotalCoinsForBonus(parser.totalCoinsForBonus);
|
||||
setCoinsStillRequiredToBuy(parser.coinsStillRequiredToBuy);
|
||||
}, []);
|
||||
|
||||
CreateMessageHook(BonusRareInfoMessageEvent, onBonusRareInfoMessageEvent);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
SendMessageHook(new GetBonusRareInfoMessageComposer());
|
||||
}, []);
|
||||
|
||||
if(!productType) return null;
|
||||
|
||||
return (
|
||||
<div className="bonus-rare widget d-flex">
|
||||
{ productType }
|
||||
<div className="bg-light-dark rounded overflow-hidden position-relative bonus-bar-container">
|
||||
<div className="d-flex justify-content-center align-items-center w-100 h-100 position-absolute small top-0">{(totalCoinsForBonus - coinsStillRequiredToBuy) + '/' + totalCoinsForBonus}</div>
|
||||
<div className="small bg-info rounded position-absolute top-0 h-100" style={{ width: ((totalCoinsForBonus - coinsStillRequiredToBuy) / totalCoinsForBonus) * 100 + '%' }}></div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
export interface BonusRareWidgetViewProps
|
||||
{}
|
@ -0,0 +1,21 @@
|
||||
import { FC } from 'react';
|
||||
import { LocalizeText } from '../../../../../utils';
|
||||
import { AvatarImageView } from '../../../../shared/avatar-image/AvatarImageView';
|
||||
import { HallOfFameItemViewProps } from './HallOfFameItemView.types';
|
||||
|
||||
export const HallOfFameItemView: FC<HallOfFameItemViewProps> = props =>
|
||||
{
|
||||
const { data = null, level = 0 } = props;
|
||||
|
||||
return (
|
||||
<div className="hof-user-container cursor-pointer">
|
||||
<div className="hof-tooltip">
|
||||
<div className="hof-tooltip-content">
|
||||
<div className="fw-bold">{ level }. { data.userName }</div>
|
||||
<div className="muted fst-italic small text-center">{ LocalizeText('landing.view.competition.hof.points', [ 'points' ], [ data.currentScore.toString() ])}</div>
|
||||
</div>
|
||||
</div>
|
||||
<AvatarImageView figure={ data.figure } direction={ 2 } />
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
import { HallOfFameEntryData } from '@nitrots/nitro-renderer';
|
||||
|
||||
export interface HallOfFameItemViewProps
|
||||
{
|
||||
data: HallOfFameEntryData;
|
||||
level: number;
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
.hall-of-fame {
|
||||
background-color: rgba($black,.3);
|
||||
border-radius: $border-radius;
|
||||
display: inline-block;
|
||||
|
||||
.hof-user-container {
|
||||
display:inline-flex;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
.hof-tooltip {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.hof-tooltip {
|
||||
position: absolute;
|
||||
display: inline;
|
||||
z-index: 2;
|
||||
display: none;
|
||||
background-color: #1c323f;
|
||||
border: 2px solid rgba($white, 0.5);
|
||||
border-radius: $border-radius;
|
||||
bottom:calc(100% - 20px);
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: auto;
|
||||
padding:2px;
|
||||
color: #fff;
|
||||
|
||||
.hof-tooltip-content {
|
||||
padding:3px;
|
||||
background-color: #3d5f6e;
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: -7px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: auto;
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
transform: rotate(45deg);
|
||||
border-color: transparent rgba($white, 0.5) rgba($white, 0.5) transparent;
|
||||
border-style: solid;
|
||||
border-width: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-image {
|
||||
position:relative;
|
||||
display:inline;
|
||||
left:0;
|
||||
top:0;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
import { CommunityGoalHallOfFameData, CommunityGoalHallOfFameMessageEvent, GetCommunityGoalHallOfFameMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { CreateMessageHook, SendMessageHook } from '../../../../../hooks/messages/message-event';
|
||||
import { HallOfFameItemView } from '../hall-of-fame-item/HallOfFameItemView';
|
||||
import { HallOfFameWidgetViewProps } from './HallOfFameWidgetView.types';
|
||||
|
||||
export const HallOfFameWidgetView: FC<HallOfFameWidgetViewProps> = props =>
|
||||
{
|
||||
const { slot = -1, conf = '' } = props;
|
||||
const [ data, setData ] = useState<CommunityGoalHallOfFameData>(null);
|
||||
|
||||
const onCommunityGoalHallOfFameMessageEvent = useCallback((event: CommunityGoalHallOfFameMessageEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setData(parser.data);
|
||||
}, []);
|
||||
|
||||
CreateMessageHook(CommunityGoalHallOfFameMessageEvent, onCommunityGoalHallOfFameMessageEvent);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
SendMessageHook(new GetCommunityGoalHallOfFameMessageComposer(conf));
|
||||
}, [ conf ]);
|
||||
|
||||
if(!data) return null;
|
||||
|
||||
return (
|
||||
<div className="hall-of-fame widget">
|
||||
{ data.hof && (data.hof.length > 0) && data.hof.map((entry, index) =>
|
||||
{
|
||||
return <HallOfFameItemView data={ entry } level={ (index + 1) } />;
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
export interface HallOfFameWidgetViewProps
|
||||
{
|
||||
slot: number;
|
||||
conf: string;
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
.promo-articles {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.promo-articles-bullet {
|
||||
border-radius: 50%;
|
||||
background-color: $white;
|
||||
border: 1px solid $white;
|
||||
height: 13px;
|
||||
width: 13px;
|
||||
margin-right: 3px;
|
||||
|
||||
&.promo-articles-bullet-active {
|
||||
background: $black;
|
||||
}
|
||||
}
|
||||
|
||||
.promo-article {
|
||||
.promo-article-image {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
margin-right: 10px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: top center;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
import { GetPromoArticlesComposer, PromoArticleData, PromoArticlesMessageEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { CreateMessageHook, SendMessageHook } from '../../../../../hooks';
|
||||
import { LocalizeText } from '../../../../../utils/LocalizeText';
|
||||
import { PromoArticleWidgetViewProps } from './PromoArticleWidgetView.types';
|
||||
|
||||
export const PromoArticleWidgetView: FC<PromoArticleWidgetViewProps> = props =>
|
||||
{
|
||||
const [articles, setArticles] = useState<PromoArticleData[]>(null);
|
||||
const [index, setIndex] = useState(0);
|
||||
|
||||
const handleSelect = (selectedIndex) =>
|
||||
{
|
||||
setIndex(selectedIndex);
|
||||
};
|
||||
|
||||
const onPromoArticlesMessageEvent = useCallback((event: PromoArticlesMessageEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
setArticles(parser.articles);
|
||||
}, []);
|
||||
|
||||
CreateMessageHook(PromoArticlesMessageEvent, onPromoArticlesMessageEvent);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
SendMessageHook(new GetPromoArticlesComposer());
|
||||
}, []);
|
||||
|
||||
if (!articles) return null;
|
||||
|
||||
return (
|
||||
<div className="promo-articles widget mb-2">
|
||||
<div className="d-flex flex-row align-items-center w-100 mb-1">
|
||||
<small className="flex-shrink-0 pe-1">{ LocalizeText('landing.view.promo.article.header') }</small>
|
||||
<hr className="w-100 my-0"/>
|
||||
</div>
|
||||
<div className="d-flex flex-row mb-1">
|
||||
{articles && (articles.length > 0) && articles.map((article, ind) =>
|
||||
<div className={`promo-articles-bullet cursor-pointer ` + (article === articles[index] ? 'promo-articles-bullet-active' : '')} key={article.id} onClick={event => handleSelect(ind)} />
|
||||
)}
|
||||
</div>
|
||||
{articles && articles[index] &&
|
||||
<div className="promo-article d-flex flex-row row mx-0">
|
||||
<div className="promo-article-image" style={ {backgroundImage: `url(${articles[index].imageUrl})`} }/>
|
||||
<div className="col-3 d-flex flex-column h-100">
|
||||
<h3 className="my-0">{articles[index].title}</h3>
|
||||
<b>{ articles[index].bodyText }</b>
|
||||
<button className="btn btn-sm mt-auto btn-gainsboro">{ articles[index].buttonText }</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
export interface PromoArticleWidgetViewProps
|
||||
{}
|
@ -0,0 +1,9 @@
|
||||
.widgetcontainer {
|
||||
.widgetcontainer-image {
|
||||
width: 150px;
|
||||
height: 150px;
|
||||
margin-right: 10px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: top center;
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
import { FC, useCallback, useMemo } from 'react';
|
||||
import { GetConfigurationManager } from '../../../../../api/core';
|
||||
import { LocalizeText } from '../../../../../utils/LocalizeText';
|
||||
import { WidgetContainerViewProps } from './WidgetContainerView.types';
|
||||
|
||||
export const WidgetContainerView: FC<WidgetContainerViewProps> = props =>
|
||||
{
|
||||
const { conf = null } = props;
|
||||
|
||||
const config = useMemo(() =>
|
||||
{
|
||||
const config = {};
|
||||
|
||||
if(!conf || !conf.length) return config;
|
||||
|
||||
let options = conf.split(",");
|
||||
|
||||
options.forEach(option =>
|
||||
{
|
||||
let [ key, value ] = option.split(':');
|
||||
|
||||
if(key && value) config[key] = value;
|
||||
});
|
||||
|
||||
return config;
|
||||
}, [ conf ]);
|
||||
|
||||
const getOption = useCallback((key: string) =>
|
||||
{
|
||||
const option = config[key];
|
||||
|
||||
if(!option) return null;
|
||||
|
||||
switch(key)
|
||||
{
|
||||
case 'image':
|
||||
return GetConfigurationManager().interpolate(option);
|
||||
}
|
||||
|
||||
return option;
|
||||
}, [ config ]);
|
||||
|
||||
return (
|
||||
<div className="widgetcontainer widget d-flex flex-row overflow-hidden">
|
||||
<div className="widgetcontainer-image flex-shrink-0" style={{ backgroundImage: `url(${getOption('image')})` }} />
|
||||
<div className="d-flex flex-column align-self-center">
|
||||
<h3 className="my-0">{LocalizeText(`landing.view.${getOption('texts')}.header`)}</h3>
|
||||
<i>{ LocalizeText(`landing.view.${getOption('texts')}.body`) }</i>
|
||||
<button className="btn btn-sm btn-gainsboro align-self-start px-3 mt-auto">{ LocalizeText(`landing.view.${getOption('texts')}.button`) }</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
export interface WidgetContainerViewProps
|
||||
{
|
||||
conf: string;
|
||||
}
|
@ -1,21 +1,23 @@
|
||||
import { AvatarScaleType, AvatarSetType } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { FC, useEffect, useRef, useState } from 'react';
|
||||
import { GetAvatarRenderManager } from '../../../api';
|
||||
import { AvatarImageViewProps } from './AvatarImageView.types';
|
||||
|
||||
export const AvatarImageView: FC<AvatarImageViewProps> = props =>
|
||||
{
|
||||
const { figure = '', gender = 'M', headOnly = false, direction = 0, scale = 1 } = props;
|
||||
|
||||
const [ avatarUrl, setAvatarUrl ] = useState<string>(null);
|
||||
const [ randomValue, setRandomValue ] = useState(-1);
|
||||
const isDisposed = useRef(false);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(randomValue) {}
|
||||
|
||||
const avatarImage = GetAvatarRenderManager().createAvatarImage(figure, AvatarScaleType.LARGE, gender, {
|
||||
resetFigure: figure => setRandomValue(Math.random()),
|
||||
resetFigure: figure => {
|
||||
if(isDisposed.current) return;
|
||||
|
||||
setRandomValue(Math.random());
|
||||
},
|
||||
dispose: () => {},
|
||||
disposed: false
|
||||
}, null);
|
||||
@ -35,6 +37,16 @@ export const AvatarImageView: FC<AvatarImageViewProps> = props =>
|
||||
avatarImage.dispose();
|
||||
}, [ figure, gender, direction, headOnly, randomValue ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
isDisposed.current = false;
|
||||
|
||||
return () =>
|
||||
{
|
||||
isDisposed.current = true;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const url = `url('${ avatarUrl }')`;
|
||||
|
||||
return <div className={ 'avatar-image scale-' + scale } style={ (avatarUrl && url.length) ? { backgroundImage: url } : {} }></div>;
|
||||
|
Loading…
Reference in New Issue
Block a user