Initial commit

This commit is contained in:
MyNameIsBatman 2021-06-22 13:40:28 -03:00
parent c2f2aeed1e
commit 351571fdbe
30 changed files with 508 additions and 49 deletions

View File

@ -0,0 +1,19 @@
import { NitroNotification } from '../../views/notification-center/utils/Notification';
import { NotificationCenterEvent } from './NotificationCenterEvent';
export class NotificationCenterAddNotificationEvent extends NotificationCenterEvent
{
private _notification: NitroNotification;
constructor(notification: NitroNotification)
{
super(NotificationCenterEvent.ADD_NOTIFICATION);
this._notification = notification;
}
public get notification(): NitroNotification
{
return this._notification;
}
}

View File

@ -5,4 +5,5 @@ export class NotificationCenterEvent extends NitroEvent
public static SHOW_NOTIFICATION_CENTER: string = 'NCE_SHOW_NOTIFICATION_CENTER'; public static SHOW_NOTIFICATION_CENTER: string = 'NCE_SHOW_NOTIFICATION_CENTER';
public static HIDE_NOTIFICATION_CENTER: string = 'NCE_HIDE_NOTIFICATION_CENTER'; public static HIDE_NOTIFICATION_CENTER: string = 'NCE_HIDE_NOTIFICATION_CENTER';
public static TOGGLE_NOTIFICATION_CENTER: string = 'NCE_TOGGLE_NOTIFICATION_CENTER'; public static TOGGLE_NOTIFICATION_CENTER: string = 'NCE_TOGGLE_NOTIFICATION_CENTER';
public static ADD_NOTIFICATION: string = 'NCE_ADD_NOTIFICATION';
} }

View File

@ -1 +1,2 @@
export * from './NotificationCenterAddNotificationEvent';
export * from './NotificationCenterEvent'; export * from './NotificationCenterEvent';

View File

@ -2,5 +2,25 @@
text-shadow: 2px 2px 0px rgba(0, 0, 0, .2) text-shadow: 2px 2px 0px rgba(0, 0, 0, .2)
} }
.nitro-close {
right: 5px;
border-radius: $border-radius;
box-shadow: inset 0 0 0 1.5px #921911, inset 0 2px rgba($white, .2);
border: 1px solid $white;
background: rgb(245,80,65);
background: linear-gradient(180deg, rgba(245,80,65,1) 0%, rgba(245,80,65,1) 50%, rgba(194,48,39,1) 50%, rgba(194,48,39,1) 100%);
cursor: pointer;
line-height: 1;
padding: 4px 6px;
&:hover {
filter: brightness(1.2);
}
&:active {
filter: brightness(0.8);
}
}
@import './card/NitroCardView'; @import './card/NitroCardView';
@import './loading-spinner/LoadingSpinnerView'; @import './loading-spinner/LoadingSpinnerView';

View File

@ -1,28 +1,4 @@
.nitro-card-header { .nitro-card-header {
min-height: $nitro-card-header-height; min-height: $nitro-card-header-height;
max-height: $nitro-card-header-height; max-height: $nitro-card-header-height;
.nitro-card-close {
right: 6px;
border-radius: $border-radius;
box-shadow: inset 0 0 0 1.5px #921911, inset 0 2px rgba($white, .2);
border: 1px solid $white;
background: rgb(245,80,65);
background: linear-gradient(180deg, rgba(245,80,65,1) 0%, rgba(245,80,65,1) 50%, rgba(194,48,39,1) 50%, rgba(194,48,39,1) 100%);
cursor: pointer;
line-height: 1;
padding: 4px 6px;
&:hover {
filter: brightness(1.2);
}
&:active {
filter: brightness(0.8);
}
}
.nitro-card-close-parent {
right: -3px;
}
} }

View File

@ -12,7 +12,7 @@ export const NitroCardHeaderView: FC<NitroCardHeaderViewProps> = props =>
<div className="row nitro-card-header"> <div className="row nitro-card-header">
<div className="d-flex justify-content-center align-items-center w-100 position-relative"> <div className="d-flex justify-content-center align-items-center w-100 position-relative">
<div className="h4 m-0 text-white text-shadow">{ headerText }</div> <div className="h4 m-0 text-white text-shadow">{ headerText }</div>
<div className="position-absolute nitro-card-close" onMouseDown={ event => event.stopPropagation() } onClick={ onCloseClick }> <div className="position-absolute nitro-close" onMouseDown={ event => event.stopPropagation() } onClick={ onCloseClick }>
<i className="fas fa-times" /> <i className="fas fa-times" />
</div> </div>
</div> </div>

View File

@ -1,8 +1,6 @@
.nitro-card-close-parent {
right: 5px;
}
.bg-tertiary-split { .bg-tertiary-split {
border: 2px solid darken($quaternary,4); border: 2px solid darken($quaternary,4);
box-shadow:0 0 0 2px $white; box-shadow:0 0 0 2px $white;
margin-left: 24px;
margin-right: 24px;
} }

View File

@ -7,15 +7,11 @@ export const NitroCardSimpleHeaderView: FC<NitroCardSimpleHeaderViewProps> = pro
return ( return (
<div className="d-flex container-fluid align-items-center bg-light pb-1"> <div className="d-flex container-fluid align-items-center bg-light pb-1">
<div className="col-1"/>
<div className="d-flex bg-tertiary-split flex-grow-1 justify-content-center align-items-center border-top-0 rounded-bottom px-2 py-1 drag-handler"> <div className="d-flex bg-tertiary-split flex-grow-1 justify-content-center align-items-center border-top-0 rounded-bottom px-2 py-1 drag-handler">
<div className="h5 m-0 text-white text-shadow">{ headerText }</div> <div className="h5 m-0 text-white text-shadow">{ headerText }</div>
</div> </div>
<div className="col-1"/> <div className="position-absolute nitro-close" onMouseDown={ event => event.stopPropagation() } onClick={ onCloseClick }>
<div className="nitro-card-close-parent"> <i className="fas fa-times" />
<div className="nitro-card-close" onClick={ onCloseClick }>
<i className="fas fa-times"/>
</div>
</div> </div>
</div> </div>
); );

View File

@ -0,0 +1,84 @@
import { HabboBroadcastMessageEvent, HotelWillShutdownEvent, ModeratorMessageEvent, MOTDNotificationEvent, NotificationDialogMessageEvent } from 'nitro-renderer';
import { FC, useCallback } from 'react';
import { CreateMessageHook } from '../../hooks/messages';
import { useNotificationCenterContext } from './context/NotificationCenterContext';
import { INotificationCenterMessageHandlerProps } from './NotificationCenterMessageHandler.types';
import { NotificationCenterActions } from './reducers/NotificationCenterReducer';
import { BroadcastMessageNotification } from './utils/BroadcastMessageNotification';
import { DialogMessageNotification } from './utils/DialogMessageNotification';
import { HotelWillShutdownNotification } from './utils/HotelWillShutdownNotification';
import { ModeratorMessageNotification } from './utils/ModeratorMessageNotification';
import { MOTDNotification } from './utils/MOTDNotification';
export const NotificationCenterMessageHandler: FC<INotificationCenterMessageHandlerProps> = props =>
{
const { dispatchNotificationCenterState = null } = useNotificationCenterContext();
const onHabboBroadcastMessageEvent = useCallback((event: HabboBroadcastMessageEvent) =>
{
const parser = event.getParser();
dispatchNotificationCenterState({
type: NotificationCenterActions.ADD_NOTIFICATION,
payload: {
notification: new BroadcastMessageNotification(parser.message)
}
});
}, [ dispatchNotificationCenterState ]);
const onNotificationDialogMessageEvent = useCallback((event: NotificationDialogMessageEvent) =>
{
const parser = event.getParser();
dispatchNotificationCenterState({
type: NotificationCenterActions.ADD_NOTIFICATION,
payload: {
notification: new DialogMessageNotification(parser.type, parser.parameters)
}
});
}, [ dispatchNotificationCenterState ]);
const onModeratorMessageEvent = useCallback((event: ModeratorMessageEvent) =>
{
const parser = event.getParser();
dispatchNotificationCenterState({
type: NotificationCenterActions.ADD_NOTIFICATION,
payload: {
notification: new ModeratorMessageNotification(parser.message, parser.link)
}
});
}, [ dispatchNotificationCenterState ]);
const onMOTDNotificationEvent = useCallback((event: MOTDNotificationEvent) =>
{
const parser = event.getParser();
dispatchNotificationCenterState({
type: NotificationCenterActions.ADD_NOTIFICATION,
payload: {
notification: new MOTDNotification(parser.messages)
}
});
}, [ dispatchNotificationCenterState ]);
const onHotelWillShutdownEvent = useCallback((event: HotelWillShutdownEvent) =>
{
const parser = event.getParser();
dispatchNotificationCenterState({
type: NotificationCenterActions.ADD_NOTIFICATION,
payload: {
notification: new HotelWillShutdownNotification(parser.minutes)
}
});
}, [ dispatchNotificationCenterState ]);
CreateMessageHook(HabboBroadcastMessageEvent, onHabboBroadcastMessageEvent);
CreateMessageHook(NotificationDialogMessageEvent, onNotificationDialogMessageEvent);
CreateMessageHook(ModeratorMessageEvent, onModeratorMessageEvent);
CreateMessageHook(MOTDNotificationEvent, onMOTDNotificationEvent);
CreateMessageHook(HotelWillShutdownEvent, onHotelWillShutdownEvent);
return null;
}

View File

@ -0,0 +1,2 @@
export interface INotificationCenterMessageHandlerProps
{}

View File

@ -11,3 +11,12 @@
height: 100%; height: 100%;
} }
} }
.nitro-notification {
width: 350px;
.content-area {
max-height: 200px;
overflow-y: auto;
}
}

View File

@ -1,14 +1,26 @@
import { FC, useCallback, useState } from 'react'; import { FC, useCallback, useReducer, useState } from 'react';
import { NotificationCenterEvent } from '../../events'; import { NotificationCenterAddNotificationEvent, NotificationCenterEvent } from '../../events';
import { useUiEvent } from '../../hooks/events'; import { useUiEvent } from '../../hooks/events';
import { TransitionAnimation } from '../../layout/transitions/TransitionAnimation'; import { TransitionAnimation } from '../../layout/transitions/TransitionAnimation';
import { TransitionAnimationTypes } from '../../layout/transitions/TransitionAnimation.types'; import { TransitionAnimationTypes } from '../../layout/transitions/TransitionAnimation.types';
import { NotificationCenterContextProvider } from './context/NotificationCenterContext';
import { NotificationCenterMessageHandler } from './NotificationCenterMessageHandler';
import { NotificationCenterViewProps } from './NotificationCenterView.types'; import { NotificationCenterViewProps } from './NotificationCenterView.types';
import { initialNotificationCenter, NotificationCenterActions, NotificationCenterReducer } from './reducers/NotificationCenterReducer';
import { BroadcastMessageNotification } from './utils/BroadcastMessageNotification';
import { ModeratorMessageNotification } from './utils/ModeratorMessageNotification';
import { MOTDNotification } from './utils/MOTDNotification';
import { BroadcastMessageView } from './views/broadcast-message/BroadcastMessageView';
import { ModeratorMessageView } from './views/moderator-message/ModeratorMessageView';
import { MOTDView } from './views/motd/MOTDView';
export const NotificationCenterView: FC<NotificationCenterViewProps> = props => export const NotificationCenterView: FC<NotificationCenterViewProps> = props =>
{ {
const [ isVisible, setIsVisible ] = useState(false); const [ isVisible, setIsVisible ] = useState(false);
const [ notificationCenterState, dispatchNotificationCenterState ] = useReducer(NotificationCenterReducer, initialNotificationCenter);
const { notifications = null } = notificationCenterState;
const onNotificationCenterEvent = useCallback((event: NotificationCenterEvent) => const onNotificationCenterEvent = useCallback((event: NotificationCenterEvent) =>
{ {
switch(event.type) switch(event.type)
@ -22,19 +34,62 @@ export const NotificationCenterView: FC<NotificationCenterViewProps> = props =>
case NotificationCenterEvent.TOGGLE_NOTIFICATION_CENTER: case NotificationCenterEvent.TOGGLE_NOTIFICATION_CENTER:
setIsVisible(value => !value); setIsVisible(value => !value);
return; return;
case NotificationCenterEvent.ADD_NOTIFICATION: {
const castedEvent = (event as NotificationCenterAddNotificationEvent);
dispatchNotificationCenterState({
type: NotificationCenterActions.ADD_NOTIFICATION,
payload: {
notification: castedEvent.notification
}
});
return;
}
} }
}, []); }, []);
useUiEvent(NotificationCenterEvent.SHOW_NOTIFICATION_CENTER, onNotificationCenterEvent); useUiEvent(NotificationCenterEvent.SHOW_NOTIFICATION_CENTER, onNotificationCenterEvent);
useUiEvent(NotificationCenterEvent.HIDE_NOTIFICATION_CENTER, onNotificationCenterEvent); useUiEvent(NotificationCenterEvent.HIDE_NOTIFICATION_CENTER, onNotificationCenterEvent);
useUiEvent(NotificationCenterEvent.TOGGLE_NOTIFICATION_CENTER, onNotificationCenterEvent); useUiEvent(NotificationCenterEvent.TOGGLE_NOTIFICATION_CENTER, onNotificationCenterEvent);
useUiEvent(NotificationCenterEvent.ADD_NOTIFICATION, onNotificationCenterEvent);
const handleButtonClick = useCallback((action: string, value: number) =>
{
if(!action) return;
switch(action)
{
case 'dismiss_alert':
dispatchNotificationCenterState({
type: NotificationCenterActions.REMOVE_NOTIFICATION,
payload: {
id: value
}
});
return;
}
}, []);
return ( return (
<TransitionAnimation className="nitro-notification-center-container" type={ TransitionAnimationTypes.SLIDE_RIGHT } inProp={ isVisible } timeout={ 300 }> <NotificationCenterContextProvider value={ { notificationCenterState, dispatchNotificationCenterState } }>
<div className="nitro-notification-center"> <NotificationCenterMessageHandler />
test <TransitionAnimation className="nitro-notification-center-container" type={ TransitionAnimationTypes.SLIDE_RIGHT } inProp={ isVisible } timeout={ 300 }>
</div> <div className="nitro-notification-center">
</TransitionAnimation>
</div>
</TransitionAnimation>
{ notifications && notifications.map(notification =>
{
if(notification instanceof BroadcastMessageNotification)
return <BroadcastMessageView key={ notification.id } notification={ notification as BroadcastMessageNotification } onCloseClick={ () => handleButtonClick('dismiss_alert', notification.id) } />
if(notification instanceof MOTDNotification)
return <MOTDView key={ notification.id } notification={ notification as MOTDNotification } onCloseClick={ () => handleButtonClick('dismiss_alert', notification.id) } />
if(notification instanceof ModeratorMessageNotification)
return <ModeratorMessageView key={ notification.id } notification={ notification as ModeratorMessageNotification } onCloseClick={ () => handleButtonClick('dismiss_alert', notification.id) } />
else
return null;
}) }
</NotificationCenterContextProvider>
); );
} }

View File

@ -2,3 +2,8 @@ export interface NotificationCenterViewProps
{ {
} }
export class NotificationViewProps
{
onCloseClick: () => void;
}

View File

@ -0,0 +1,14 @@
import { createContext, FC, useContext } from 'react';
import { INotificationCenterContext, NotificationCenterContextProps } from './NotificationCenterContext.types';
const NotificationCenterContext = createContext<INotificationCenterContext>({
notificationCenterState: null,
dispatchNotificationCenterState: null
});
export const NotificationCenterContextProvider: FC<NotificationCenterContextProps> = props =>
{
return <NotificationCenterContext.Provider value={ props.value }>{ props.children }</NotificationCenterContext.Provider>
}
export const useNotificationCenterContext = () => useContext(NotificationCenterContext);

View File

@ -0,0 +1,13 @@
import { Dispatch, ProviderProps } from 'react';
import { INotificationCenterAction, INotificationCenterState } from '../reducers/NotificationCenterReducer';
export interface INotificationCenterContext
{
notificationCenterState: INotificationCenterState;
dispatchNotificationCenterState: Dispatch<INotificationCenterAction>;
}
export interface NotificationCenterContextProps extends ProviderProps<INotificationCenterContext>
{
}

View File

@ -0,0 +1,60 @@
import { Reducer } from 'react';
import { NitroNotification } from '../utils/Notification';
export interface INotificationCenterState
{
notifications: NitroNotification[];
}
export interface INotificationCenterAction
{
type: string;
payload: {
id?: number;
notification?: NitroNotification;
};
}
export class NotificationCenterActions
{
public static ADD_NOTIFICATION: string = 'NCA_ADD_NOTIFICATION';
public static REMOVE_NOTIFICATION: string = 'NCA_REMOVE_NOTIFICATION';
}
export const initialNotificationCenter: INotificationCenterState = {
notifications: null
}
export const NotificationCenterReducer: Reducer<INotificationCenterState, INotificationCenterAction> = (state, action) =>
{
switch(action.type)
{
case NotificationCenterActions.ADD_NOTIFICATION: {
const notification = (action.payload.notification || null);
if(!notification) return state;
if(state.notifications) return {...state, notifications: [...state.notifications, notification]};
return {...state, notifications: [notification]};
}
case NotificationCenterActions.REMOVE_NOTIFICATION: {
const id = (action.payload.id || null);
if(!id) return state;
if(!state.notifications) return state;
const notifications = Array.from(state.notifications);
const notificationIndex = notifications.findIndex(notification => notification.id === id);
if(notificationIndex === -1) return state;
notifications.splice(notificationIndex, 1);
return {...state, notifications};
}
default:
return state;
}
}

View File

@ -0,0 +1,4 @@
import { NitroNotification } from './Notification';
export class BroadcastMessageNotification extends NitroNotification
{}

View File

@ -0,0 +1,24 @@
import { NitroNotification } from './Notification';
export class DialogMessageNotification extends NitroNotification
{
private _type: string;
private _parameters: Map<string, string>;
constructor(type: string, parameters: Map<string, string>)
{
super();
this._type = type;
this._parameters = parameters;
}
public get type(): string
{
return this._type;
}
public get parameters(): Map<string, string>
{
return this._parameters;
}
}

View File

@ -0,0 +1,17 @@
import { NitroNotification } from './Notification';
export class HotelWillShutdownNotification extends NitroNotification
{
private _minutes: number;
constructor(minutes: number)
{
super();
this._minutes = minutes;
}
public get minutes(): number
{
return this._minutes;
}
}

View File

@ -0,0 +1,19 @@
import { NitroNotification } from './Notification';
export class MOTDNotification extends NitroNotification
{
private _messages: string[];
constructor(messages: string[])
{
super();
this._messages = [];
for(const message of messages) this._messages.push(message.replace(/\r\n|\r|\n/g, '<br />'));
}
public get messages(): string[]
{
return this._messages;
}
}

View File

@ -0,0 +1,17 @@
import { NitroNotification } from './Notification';
export class ModeratorMessageNotification extends NitroNotification
{
private _link: string;
constructor(message: string, link: string)
{
super(message);
this._link = link;
}
public get link(): string
{
return this._link;
}
}

View File

@ -0,0 +1,31 @@
export class NitroNotification
{
public static CURRENT_ID: number = 0;
private _id: number;
private _title: string;
private _message: string;
constructor(message: string = null, title: string = null)
{
this._id = ++NitroNotification.CURRENT_ID;
this._title = title;
if(message) this._message = message.replace(/\r\n|\r|\n/g, '<br />');
}
public get id(): number
{
return this._id;
}
public get title(): string
{
return this._title;
}
public get message(): string
{
return this._message;
}
}

View File

@ -0,0 +1,23 @@
import { FC } from 'react';
import { NitroCardContentView, NitroCardView } from '../../../../layout';
import { NitroCardSimpleHeaderView } from '../../../../layout/card/simple-header';
import { LocalizeText } from '../../../../utils/LocalizeText';
import { BroadcastMessageViewProps } from './BroadcastMessageView.types';
export const BroadcastMessageView: FC<BroadcastMessageViewProps> = props =>
{
const { notification = null, onCloseClick = null } = props;
if(!notification) return null;
return (
<NitroCardView className="nitro-notification">
<NitroCardSimpleHeaderView headerText={ LocalizeText('mod.alert.title') } onCloseClick={ onCloseClick } />
<NitroCardContentView>
<div className="text-black">
<div dangerouslySetInnerHTML={ {__html: notification.message } } />
</div>
</NitroCardContentView>
</NitroCardView>
);
};

View File

@ -0,0 +1,7 @@
import { NotificationViewProps } from '../../NotificationCenterView.types';
import { BroadcastMessageNotification } from './../../utils/BroadcastMessageNotification';
export class BroadcastMessageViewProps extends NotificationViewProps
{
notification: BroadcastMessageNotification;
}

View File

@ -0,0 +1,26 @@
import { FC } from 'react';
import { NitroCardContentView, NitroCardView } from '../../../../layout';
import { NitroCardSimpleHeaderView } from '../../../../layout/card/simple-header';
import { LocalizeText } from '../../../../utils/LocalizeText';
import { ModeratorMessageViewProps } from './ModeratorMessageView.types';
export const ModeratorMessageView: FC<ModeratorMessageViewProps> = props =>
{
const { notification = null, onCloseClick = null } = props;
if(!notification) return null;
return (
<NitroCardView className="nitro-notification">
<NitroCardSimpleHeaderView headerText={ LocalizeText('mod.alert.title') } onCloseClick={ onCloseClick } />
<NitroCardContentView>
<div className="text-black">
<div dangerouslySetInnerHTML={ {__html: notification.message } } />
<div className="fw-bold text-center">
<a href={ notification.link } rel="noreferrer" target="_blank" onClick={ onCloseClick }>{ LocalizeText('mod.alert.link') }</a>
</div>
</div>
</NitroCardContentView>
</NitroCardView>
);
};

View File

@ -0,0 +1,7 @@
import { NotificationViewProps } from '../../NotificationCenterView.types';
import { ModeratorMessageNotification } from './../../utils/ModeratorMessageNotification';
export class ModeratorMessageViewProps extends NotificationViewProps
{
notification: ModeratorMessageNotification;
}

View File

@ -0,0 +1,26 @@
import { FC } from 'react';
import { NitroCardContentView, NitroCardView } from '../../../../layout';
import { NitroCardSimpleHeaderView } from '../../../../layout/card/simple-header';
import { LocalizeText } from '../../../../utils/LocalizeText';
import { MOTDViewProps } from './MOTDView.types';
export const MOTDView: FC<MOTDViewProps> = props =>
{
const { notification = null, onCloseClick = null } = props;
if(!notification) return null;
return (
<NitroCardView className="nitro-notification">
<NitroCardSimpleHeaderView headerText={ LocalizeText('mod.alert.title') } onCloseClick={ onCloseClick } />
<NitroCardContentView>
<div className="text-black">
{ notification.messages.map((message, index) =>
{
return <div key={ index } dangerouslySetInnerHTML={ {__html: message } } />
}) }
</div>
</NitroCardContentView>
</NitroCardView>
);
};

View File

@ -0,0 +1,7 @@
import { NotificationViewProps } from '../../NotificationCenterView.types';
import { MOTDNotification } from './../../utils/MOTDNotification';
export class MOTDViewProps extends NotificationViewProps
{
notification: MOTDNotification;
}

View File

@ -1,5 +1,5 @@
.nitro-camera-capture { .nitro-camera-capture {
.nitro-card-close-parent { .nitro-close {
top: 7px; top: 7px;
right: 10px; right: 10px;
} }

View File

@ -46,7 +46,7 @@ export const CameraWidgetCaptureView: FC<CameraWidgetCaptureViewProps> = props =
} }
cameraWidgetContext.setCameraRoll([ ...remainingRoll, image ]); cameraWidgetContext.setCameraRoll([ ...remainingRoll, image ]);
}, [ cameraWidgetContext.cameraRoll, cameraWidgetContext.selectedPictureIndex ]); }, [ cameraWidgetContext ]);
const processAction = useCallback((type: string, value: string | number = null) => const processAction = useCallback((type: string, value: string | number = null) =>
{ {
@ -73,16 +73,14 @@ export const CameraWidgetCaptureView: FC<CameraWidgetCaptureViewProps> = props =
onCloseClick(); onCloseClick();
return; return;
} }
}, [ cameraWidgetContext.selectedPictureIndex, cameraWidgetContext.cameraRoll, onEditClick, onCloseClick ]); }, [ takePicture, cameraWidgetContext, onEditClick, onCloseClick ]);
return ( return (
<DraggableWindow handle=".nitro-camera-capture"> <DraggableWindow handle=".nitro-camera-capture">
<div className="nitro-camera-capture d-flex flex-column justify-content-center align-items-center"> <div className="nitro-camera-capture d-flex flex-column justify-content-center align-items-center">
<div className="camera-canvas"> <div className="camera-canvas">
<div className="nitro-card-close-parent"> <div className="position-absolute nitro-close" onClick={ event => processAction('close') }>
<div className="nitro-card-close" onClick={ event => processAction('close') }> <i className="fas fa-times"/>
<i className="fas fa-times"/>
</div>
</div> </div>
<div ref={ cameraFrameRef } className={'camera-frame ' + classNames({'bg': cameraWidgetContext.selectedPictureIndex > -1}) }> <div ref={ cameraFrameRef } className={'camera-frame ' + classNames({'bg': cameraWidgetContext.selectedPictureIndex > -1}) }>
{ cameraWidgetContext.selectedPictureIndex > -1 && <div> { cameraWidgetContext.selectedPictureIndex > -1 && <div>